diff options
Diffstat (limited to 'src/fatlabel.c')
-rw-r--r-- | src/fatlabel.c | 298 |
1 files changed, 226 insertions, 72 deletions
diff --git a/src/fatlabel.c b/src/fatlabel.c index 9268ddb..ddb8020 100644 --- a/src/fatlabel.c +++ b/src/fatlabel.c @@ -4,7 +4,8 @@ Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2007 Red Hat, Inc. Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> - Copyright (C) 2015 Andreas Bombe <aeb@debian.org> + Copyright (C) 2015-2017 Andreas Bombe <aeb@debian.org> + Copyright (C) 2017-2018 Pali Rohár <pali.rohar@gmail.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,10 +26,13 @@ #include "version.h" +#include <stdbool.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdlib.h> +#include <limits.h> #include <unistd.h> #include <getopt.h> #include <ctype.h> @@ -40,106 +44,256 @@ #include "fat.h" #include "file.h" #include "check.h" +#include "charconv.h" -int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; -int atari_format = 0; +int rw = 0, list = 0, test = 0, verbose = 0, no_spaces_in_sfns = 0; +long fat_table = 0; unsigned n_files = 0; void *mem_queue = NULL; -static void usage(int error) + +static void handle_label(bool change, bool reset, const char *device, char *newlabel) { - FILE *f = error ? stderr : stdout; - int status = error ? 1 : 0; + DOS_FS fs = { 0 }; + off_t offset; + DIR_ENT de; - fprintf(f, "usage: fatlabel device [label]\n"); - exit(status); -} + char label[12] = { 0 }; + size_t len; + int ret; + int i; -/* - * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant - * of MS-DOS filesystem by default. - */ -static void check_atari(void) -{ -#ifdef __mc68000__ - FILE *f; - char line[128], *p; + if (change) { + len = mbstowcs(NULL, newlabel, 0); + if (len != (size_t)-1 && len > 11) { + fprintf(stderr, + "fatlabel: labels can be no longer than 11 characters\n"); + exit(1); + } - if (!(f = fopen("/proc/hardware", "r"))) { - perror("/proc/hardware"); - return; + if (!local_string_to_dos_string(label, newlabel, 12)) { + fprintf(stderr, + "fatlabel: error when processing label\n"); + exit(1); + } + + for (i = strlen(label); i < 11; ++i) + label[i] = ' '; + label[11] = 0; + + ret = validate_volume_label(label); + if (ret & 0x1) { + fprintf(stderr, + "fatlabel: warning - lowercase labels might not work properly on some systems\n"); + } + if (ret & 0x2) { + fprintf(stderr, + "fatlabel: labels with characters below 0x20 are not allowed\n"); + exit(1); + } + if (ret & 0x4) { + fprintf(stderr, + "fatlabel: labels with characters *?.,;:/\\|+=<>[]\" are not allowed\n"); + exit(1); + } + if (ret & 0x08) { + fprintf(stderr, + "fatlabel: labels can't be empty or white space only\n"); + exit(1); + } + if (ret & 0x10) { + fprintf(stderr, + "fatlabel: labels can't start with a space character\n"); + exit(1); + } } - while (fgets(line, sizeof(line), f)) { - if (strncmp(line, "Model:", 6) == 0) { - p = line + 6; - p += strspn(p, " \t"); - if (strncmp(p, "Atari ", 6) == 0) - atari_format = 1; - break; + fs_open(device, rw); + read_boot(&fs); + + if (!change && !reset) { + if (fs.fat_bits == 32) + read_fat(&fs, 0); + + offset = find_volume_de(&fs, &de); + if (offset != 0) { + if (de.name[0] == 0x05) + de.name[0] = 0xe5; + printf("%s\n", pretty_label((char *)de.name)); } + + if (fs.fat_bits == 32) + release_fat(&fs); + + exit(0); } - fclose(f); -#endif + + if (fs.fat_bits == 32) + read_fat(&fs, 1); + + if (!reset) + write_label(&fs, label); + else + remove_label(&fs); + + if (fs.fat_bits == 32) + release_fat(&fs); } -int main(int argc, char *argv[]) + +static void handle_volid(bool change, bool reset, const char *device, const char *newserial) { DOS_FS fs = { 0 }; - rw = 0; - - int i; + char *tmp; + long long conversion; + uint32_t serial = 0; - char *device = NULL; - char label[12] = { 0 }; + if (change) { + errno = 0; + conversion = strtoll(newserial, &tmp, 16); - off_t offset; - DIR_ENT de; + if (!*newserial || isspace(*newserial) || *tmp || conversion < 0) { + fprintf(stderr, "fatlabel: volume ID must be a hexadecimal number\n"); + exit(1); + } + if (conversion > UINT32_MAX) { + fprintf(stderr, "fatlabel: given volume ID does not fit in 32 bit\n"); + exit(1); + } + if (errno) { + fprintf(stderr, "fatlabel: parsing volume ID failed (%s)\n", strerror(errno)); + exit(1); + } - check_atari(); + serial = conversion; + } - if (argc < 2 || argc > 3) - usage(1); + if (reset) + serial = generate_volume_id(); - if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) - usage(0); - else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { - printf("fatlabel " VERSION " (" VERSION_DATE ")\n"); + fs_open(device, rw); + read_boot(&fs); + if (!change && !reset) { + printf("%08x\n", fs.serial); exit(0); } - device = argv[1]; - if (argc == 3) { - strncpy(label, argv[2], 11); - if (strlen(argv[2]) > 11) { - fprintf(stderr, - "fatlabel: labels can be no longer than 11 characters\n"); - exit(1); - } - for (i = 0; label[i] && i < 11; i++) - /* don't know if here should be more strict !uppercase(label[i]) */ - if (islower(label[i])) { - fprintf(stderr, - "fatlabel: warning - lowercase labels might not work properly with DOS or Windows\n"); + write_serial(&fs, serial); +} + + +static void usage(int error, int usage_only) +{ + FILE *f = error ? stderr : stdout; + int status = error ? 1 : 0; + + fprintf(f, "Usage: fatlabel [OPTIONS] DEVICE [NEW]\n"); + if (usage_only) + exit(status); + + fprintf(f, "Change the FAT filesystem label or serial on DEVICE to NEW or display the\n"); + fprintf(f, "existing label or serial if NEW is not given.\n"); + fprintf(f, "\n"); + fprintf(f, "Options:\n"); + fprintf(f, " -i, --volume-id Work on serial number instead of label\n"); + fprintf(f, " -r, --reset Remove label or generate new serial number\n"); + fprintf(f, " -c N, --codepage=N use DOS codepage N to encode/decode label (default: %d)\n", DEFAULT_DOS_CODEPAGE); + fprintf(f, " -V, --version Show version number and terminate\n"); + fprintf(f, " -h, --help Print this message and terminate\n"); + exit(status); +} + + +int main(int argc, char *argv[]) +{ + const struct option long_options[] = { + {"volume-id", no_argument, NULL, 'i'}, + {"reset", no_argument, NULL, 'r'}, + {"codepage", required_argument, NULL, 'c'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {0,} + }; + bool change; + bool reset = false; + bool volid_mode = false; + char *device = NULL; + char *new = NULL; + char *tmp; + long codepage; + int c; + + check_atari(); + + while ((c = getopt_long(argc, argv, "irc:Vh", long_options, NULL)) != -1) { + switch (c) { + case 'i': + volid_mode = 1; break; - } - rw = 1; + + case 'r': + reset = true; + break; + + case 'c': + errno = 0; + codepage = strtol(optarg, &tmp, 10); + if (!*optarg || isspace(*optarg) || *tmp || errno || codepage < 0 || codepage > INT_MAX) { + fprintf(stderr, "Invalid codepage : %s\n", optarg); + usage(1, 0); + } + if (!set_dos_codepage(codepage)) + usage(1, 0); + break; + + case 'V': + printf("fatlabel " VERSION " (" VERSION_DATE ")\n"); + exit(0); + break; + + case 'h': + usage(0, 0); + break; + + case '?': + usage(1, 0); + exit(1); + + default: + fprintf(stderr, + "Internal error: getopt_long() returned unexpected value %d\n", c); + exit(2); + } } - fs_open(device, rw); - read_boot(&fs); - if (fs.fat_bits == 32) - read_fat(&fs); - if (!rw) { - offset = find_volume_de(&fs, &de); - if (offset == 0) - fprintf(stdout, "%s\n", fs.label); - else - fprintf(stdout, "%.8s%.3s\n", de.name, de.name + 8); - exit(0); + if (!set_dos_codepage(-1)) /* set default codepage if none was given in command line */ + exit(1); + + if (optind == argc - 2) { + change = true; + } else if (optind == argc - 1) { + change = false; + } else { + usage(1, 1); } - write_label(&fs, label); + if (change || reset) + rw = 1; + + if (change && reset) { + fprintf(stderr, "fatlabel: giving new value with --reset not allowed\n"); + exit(1); + } + + device = argv[optind++]; + if (change) + new = argv[optind]; + + if (!volid_mode) + handle_label(change, reset, device, new); + else + handle_volid(change, reset, device, new); + fs_close(rw); return 0; } |