summaryrefslogtreecommitdiff
path: root/src/fatlabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fatlabel.c')
-rw-r--r--src/fatlabel.c298
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;
}