Imported Upstream version 4.2
[platform/upstream/dosfstools.git] / src / fatlabel.c
1 /* fatlabel.c - User interface
2
3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5    Copyright (C) 2007 Red Hat, Inc.
6    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
7    Copyright (C) 2015-2017 Andreas Bombe <aeb@debian.org>
8    Copyright (C) 2017-2018 Pali Rohár <pali.rohar@gmail.com>
9
10    This program is free software: you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation, either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23    The complete text of the GNU General Public License
24    can be found in /usr/share/common-licenses/GPL-3 file.
25 */
26
27 #include "version.h"
28
29 #include <stdbool.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <unistd.h>
37 #include <getopt.h>
38 #include <ctype.h>
39
40 #include "common.h"
41 #include "fsck.fat.h"
42 #include "io.h"
43 #include "boot.h"
44 #include "fat.h"
45 #include "file.h"
46 #include "check.h"
47 #include "charconv.h"
48
49 int rw = 0, list = 0, test = 0, verbose = 0, no_spaces_in_sfns = 0;
50 long fat_table = 0;
51 unsigned n_files = 0;
52 void *mem_queue = NULL;
53
54
55 static void handle_label(bool change, bool reset, const char *device, char *newlabel)
56 {
57     DOS_FS fs = { 0 };
58     off_t offset;
59     DIR_ENT de;
60
61     char label[12] = { 0 };
62     size_t len;
63     int ret;
64     int i;
65
66     if (change) {
67         len = mbstowcs(NULL, newlabel, 0);
68         if (len != (size_t)-1 && len > 11) {
69             fprintf(stderr,
70                     "fatlabel: labels can be no longer than 11 characters\n");
71             exit(1);
72         }
73
74         if (!local_string_to_dos_string(label, newlabel, 12)) {
75             fprintf(stderr,
76                     "fatlabel: error when processing label\n");
77             exit(1);
78         }
79
80         for (i = strlen(label); i < 11; ++i)
81             label[i] = ' ';
82         label[11] = 0;
83
84         ret = validate_volume_label(label);
85         if (ret & 0x1) {
86             fprintf(stderr,
87                     "fatlabel: warning - lowercase labels might not work properly on some systems\n");
88         }
89         if (ret & 0x2) {
90             fprintf(stderr,
91                     "fatlabel: labels with characters below 0x20 are not allowed\n");
92             exit(1);
93         }
94         if (ret & 0x4) {
95             fprintf(stderr,
96                     "fatlabel: labels with characters *?.,;:/\\|+=<>[]\" are not allowed\n");
97             exit(1);
98         }
99         if (ret & 0x08) {
100             fprintf(stderr,
101                     "fatlabel: labels can't be empty or white space only\n");
102             exit(1);
103         }
104         if (ret & 0x10) {
105             fprintf(stderr,
106                     "fatlabel: labels can't start with a space character\n");
107             exit(1);
108         }
109     }
110
111     fs_open(device, rw);
112     read_boot(&fs);
113
114     if (!change && !reset) {
115         if (fs.fat_bits == 32)
116             read_fat(&fs, 0);
117
118         offset = find_volume_de(&fs, &de);
119         if (offset != 0) {
120             if (de.name[0] == 0x05)
121                 de.name[0] = 0xe5;
122             printf("%s\n", pretty_label((char *)de.name));
123         }
124
125         if (fs.fat_bits == 32)
126             release_fat(&fs);
127
128         exit(0);
129     }
130
131     if (fs.fat_bits == 32)
132         read_fat(&fs, 1);
133
134     if (!reset)
135         write_label(&fs, label);
136     else
137         remove_label(&fs);
138
139     if (fs.fat_bits == 32)
140         release_fat(&fs);
141 }
142
143
144 static void handle_volid(bool change, bool reset, const char *device, const char *newserial)
145 {
146     DOS_FS fs = { 0 };
147     char *tmp;
148     long long conversion;
149     uint32_t serial = 0;
150
151     if (change) {
152         errno = 0;
153         conversion = strtoll(newserial, &tmp, 16);
154
155         if (!*newserial || isspace(*newserial) || *tmp || conversion < 0) {
156             fprintf(stderr, "fatlabel: volume ID must be a hexadecimal number\n");
157             exit(1);
158         }
159         if (conversion > UINT32_MAX) {
160             fprintf(stderr, "fatlabel: given volume ID does not fit in 32 bit\n");
161             exit(1);
162         }
163         if (errno) {
164             fprintf(stderr, "fatlabel: parsing volume ID failed (%s)\n", strerror(errno));
165             exit(1);
166         }
167
168         serial = conversion;
169     }
170
171     if (reset)
172         serial = generate_volume_id();
173
174     fs_open(device, rw);
175     read_boot(&fs);
176     if (!change && !reset) {
177         printf("%08x\n", fs.serial);
178         exit(0);
179     }
180
181     write_serial(&fs, serial);
182 }
183
184
185 static void usage(int error, int usage_only)
186 {
187     FILE *f = error ? stderr : stdout;
188     int status = error ? 1 : 0;
189
190     fprintf(f, "Usage: fatlabel [OPTIONS] DEVICE [NEW]\n");
191     if (usage_only)
192         exit(status);
193
194     fprintf(f, "Change the FAT filesystem label or serial on DEVICE to NEW or display the\n");
195     fprintf(f, "existing label or serial if NEW is not given.\n");
196     fprintf(f, "\n");
197     fprintf(f, "Options:\n");
198     fprintf(f, "  -i, --volume-id     Work on serial number instead of label\n");
199     fprintf(f, "  -r, --reset         Remove label or generate new serial number\n");
200     fprintf(f, "  -c N, --codepage=N  use DOS codepage N to encode/decode label (default: %d)\n", DEFAULT_DOS_CODEPAGE);
201     fprintf(f, "  -V, --version       Show version number and terminate\n");
202     fprintf(f, "  -h, --help          Print this message and terminate\n");
203     exit(status);
204 }
205
206
207 int main(int argc, char *argv[])
208 {
209     const struct option long_options[] = {
210         {"volume-id", no_argument, NULL, 'i'},
211         {"reset",     no_argument, NULL, 'r'},
212         {"codepage",  required_argument, NULL, 'c'},
213         {"version",   no_argument, NULL, 'V'},
214         {"help",      no_argument, NULL, 'h'},
215         {0,}
216     };
217     bool change;
218     bool reset = false;
219     bool volid_mode = false;
220     char *device = NULL;
221     char *new = NULL;
222     char *tmp;
223     long codepage;
224     int c;
225
226     check_atari();
227
228     while ((c = getopt_long(argc, argv, "irc:Vh", long_options, NULL)) != -1) {
229         switch (c) {
230             case 'i':
231                 volid_mode = 1;
232                 break;
233
234             case 'r':
235                 reset = true;
236                 break;
237
238             case 'c':
239                 errno = 0;
240                 codepage = strtol(optarg, &tmp, 10);
241                 if (!*optarg || isspace(*optarg) || *tmp || errno || codepage < 0 || codepage > INT_MAX) {
242                     fprintf(stderr, "Invalid codepage : %s\n", optarg);
243                     usage(1, 0);
244                 }
245                 if (!set_dos_codepage(codepage))
246                     usage(1, 0);
247                 break;
248
249             case 'V':
250                 printf("fatlabel " VERSION " (" VERSION_DATE ")\n");
251                 exit(0);
252                 break;
253
254             case 'h':
255                 usage(0, 0);
256                 break;
257
258             case '?':
259                 usage(1, 0);
260                 exit(1);
261
262             default:
263                 fprintf(stderr,
264                         "Internal error: getopt_long() returned unexpected value %d\n", c);
265                 exit(2);
266         }
267     }
268
269     if (!set_dos_codepage(-1))  /* set default codepage if none was given in command line */
270         exit(1);
271
272     if (optind == argc - 2) {
273         change = true;
274     } else if (optind == argc - 1) {
275         change = false;
276     } else {
277         usage(1, 1);
278     }
279
280     if (change || reset)
281         rw = 1;
282
283     if (change && reset) {
284         fprintf(stderr, "fatlabel: giving new value with --reset not allowed\n");
285         exit(1);
286     }
287
288     device = argv[optind++];
289     if (change)
290         new = argv[optind];
291
292     if (!volid_mode)
293         handle_label(change, reset, device, new);
294     else
295         handle_volid(change, reset, device, new);
296
297     fs_close(rw);
298     return 0;
299 }