// doslabel.c -- read/set the volume labels of DOS partitions by Pierre-Marie Baty <pm@pmbaty.com>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
int main (int argc, char **argv)
{
// program entrypoint
typedef struct __attribute__((packed)) fatfs_header_s
{
unsigned char bootstrap_code[3]; // offset 0
char oem_name_and_version[8]; // offset 3
unsigned short bytes_per_sector; // offset 11
unsigned char sectors_per_cluster; // offset 13
unsigned short reserved_sectors; // offset 14
unsigned char fat_copies; // offset 16
unsigned short rootdir_entries; // offset 17
unsigned short total_sectors; // offset 19
unsigned char media_type; // offset 21
unsigned short sectors_per_fat; // offset 22
unsigned short sectors_per_track; // offset 24
unsigned short heads; // offset 26
unsigned short hidden_sectors; // offset 28;
} fatfs_header_t;
unsigned char first_sector[512];
fatfs_header_t *fat_header;
const char *dev_pathname;
size_t label_offset;
size_t char_index;
char *new_label;
char label[12];
FILE *fp;
// consistency checks
if ((argc < 2) || (*(argv[1]) == '-'))
{
fprintf (stderr
, "usage: doslabel <device> [new-label]\n");
}
dev_pathname = argv[1]; // read arguments
new_label = (argc > 2 ? argv[2] : NULL);
// open and read the first sector of the passed device pathname
if (((fp
= fopen (dev_pathname
, "r+b")) == NULL
) || (fread (first_sector
, 512, 1, fp
) != 1))
{
fprintf (stderr
, "error: can't read first sector of %s: %s\n", dev_pathname
, strerror (errno
));
}
// consistency checks
fat_header = (fatfs_header_t *) first_sector;
// is it a FAT12/FAT16 or FAT32 BPB ?
if ((memcmp (&first_sector
[510], "\x55\xaa", 2) == 0)
&& ( (fat_header->bytes_per_sector == 512)
|| (fat_header->bytes_per_sector == 1024)
|| (fat_header->bytes_per_sector == 2048)
|| (fat_header->bytes_per_sector == 4096))
&& ( (fat_header->sectors_per_cluster == 1)
|| (fat_header->sectors_per_cluster == 2)
|| (fat_header->sectors_per_cluster == 4)
|| (fat_header->sectors_per_cluster == 8)
|| (fat_header->sectors_per_cluster == 16)
|| (fat_header->sectors_per_cluster == 32)
|| (fat_header->sectors_per_cluster == 64)
|| (fat_header->sectors_per_cluster == 128))
&& ( (fat_header->reserved_sectors == 1)
|| (fat_header->reserved_sectors == 32)))
label_offset = (fat_header->sectors_per_fat != 0 ? 43 : 71);
else
{
// volume labels in exFAT filesystems are actually directory entries with a special flag
// we don't support that at the moment (FIXME)
// that's fine, because EFI partitions can't be exFAT -- only FAT12/16.
fprintf (stderr
, "error: only FAT12/FAT16/FAT32 filesystems are supported\n");
}
// do we want to read or set the volume label ?
if (new_label == NULL)
{
// read the label and strip it from trailing spaces
memcpy (label
, &first_sector
[label_offset
], 11);
label[11] = 0;
for (char_index = 10; char_index > 0; char_index--)
if (label[char_index] == ' ')
label[char_index] = 0;
else
break;
if (label[char_index] == ' ')
label[char_index] = 0;
// print it to the standard output
}
else if (argc == 3)
{
// write a new DOS partition label
fseek (fp
, label_offset
, SEEK_SET
); // can't fail
memset (label
, ' ', sizeof (label
));
if (fwrite (label
, 11, 1, fp
) != 1)
{
fprintf (stderr
, "error: can't write new volume label to %s: %s\n", dev_pathname
, strerror (errno
));
}
}
}