blob: cde594dab083a3956acecef7161775b95ed9d6c8
1 | /** |
2 | * ntfslabel - Part of the Linux-NTFS project. |
3 | * |
4 | * Copyright (c) 2002 Matthew J. Fanto |
5 | * Copyright (c) 2002-2005 Anton Altaparmakov |
6 | * Copyright (c) 2002-2003 Richard Russon |
7 | * Copyright (c) 2012 Jean-Pierre Andre |
8 | * |
9 | * This utility will display/change the label on an NTFS partition. |
10 | * |
11 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or |
14 | * (at your option) any later version. |
15 | * |
16 | * This program is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU General Public License |
22 | * along with this program (in the main directory of the Linux-NTFS |
23 | * distribution in the file COPYING); if not, write to the Free Software |
24 | * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
25 | */ |
26 | |
27 | #include "config.h" |
28 | |
29 | #ifdef HAVE_STDLIB_H |
30 | #include <stdlib.h> |
31 | #endif |
32 | #ifdef HAVE_STDIO_H |
33 | #include <stdio.h> |
34 | #endif |
35 | #ifdef HAVE_STRING_H |
36 | #include <string.h> |
37 | #endif |
38 | #ifdef HAVE_ERRNO_H |
39 | #include <errno.h> |
40 | #endif |
41 | #ifdef HAVE_LOCALE_H |
42 | #include <locale.h> |
43 | #endif |
44 | #ifdef HAVE_GETOPT_H |
45 | #include <getopt.h> |
46 | #endif |
47 | #ifdef HAVE_UNISTD_H |
48 | #include <unistd.h> |
49 | #endif |
50 | |
51 | #include "debug.h" |
52 | #include "mft.h" |
53 | #include "utils.h" |
54 | /* #include "version.h" */ |
55 | #include "logging.h" |
56 | #include "misc.h" |
57 | |
58 | static const char *EXEC_NAME = "ntfslabel"; |
59 | |
60 | static struct options { |
61 | char *device; /* Device/File to work with */ |
62 | char *label; /* Set the label to this */ |
63 | int quiet; /* Less output */ |
64 | int verbose; /* Extra output */ |
65 | int force; /* Override common sense */ |
66 | int new_serial; /* Change the serial number */ |
67 | long long serial; /* Forced serial number value */ |
68 | int noaction; /* Do not write to disk */ |
69 | } opts; |
70 | |
71 | /** |
72 | * version - Print version information about the program |
73 | * |
74 | * Print a copyright statement and a brief description of the program. |
75 | * |
76 | * Return: none |
77 | */ |
78 | static void version(void) |
79 | { |
80 | ntfs_log_info("\n%s v%s (libntfs-3g) - Display, or set, the label for an " |
81 | "NTFS Volume.\n\n", EXEC_NAME, VERSION); |
82 | ntfs_log_info("Copyright (c)\n"); |
83 | ntfs_log_info(" 2002 Matthew J. Fanto\n"); |
84 | ntfs_log_info(" 2002-2005 Anton Altaparmakov\n"); |
85 | ntfs_log_info(" 2002-2003 Richard Russon\n"); |
86 | ntfs_log_info(" 2012 Jean-Pierre Andre\n"); |
87 | ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); |
88 | } |
89 | |
90 | /** |
91 | * usage - Print a list of the parameters to the program |
92 | * |
93 | * Print a list of the parameters and options for the program. |
94 | * |
95 | * Return: none |
96 | */ |
97 | static void usage(void) |
98 | { |
99 | ntfs_log_info("\nUsage: %s [options] device [label]\n" |
100 | " -n, --no-action Do not write to disk\n" |
101 | " -f, --force Use less caution\n" |
102 | " --new-serial Set a new serial number\n" |
103 | " --new-half-serial Set a partial new serial number\n" |
104 | " -q, --quiet Less output\n" |
105 | " -v, --verbose More output\n" |
106 | " -V, --version Display version information\n" |
107 | " -h, --help Display this help\n\n", |
108 | EXEC_NAME); |
109 | ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); |
110 | } |
111 | |
112 | /** |
113 | * parse_options - Read and validate the programs command line |
114 | * |
115 | * Read the command line, verify the syntax and parse the options. |
116 | * This function is very long, but quite simple. |
117 | * |
118 | * Return: 1 Success |
119 | * 0 Error, one or more problems |
120 | */ |
121 | static int parse_options(int argc, char *argv[]) |
122 | { |
123 | static const char *sopt = "-fh?IinqvV"; |
124 | static const struct option lopt[] = { |
125 | { "force", no_argument, NULL, 'f' }, |
126 | { "help", no_argument, NULL, 'h' }, |
127 | { "new-serial", optional_argument, NULL, 'I' }, |
128 | { "new-half-serial", optional_argument, NULL, 'i' }, |
129 | { "no-action", no_argument, NULL, 'n' }, |
130 | { "quiet", no_argument, NULL, 'q' }, |
131 | { "verbose", no_argument, NULL, 'v' }, |
132 | { "version", no_argument, NULL, 'V' }, |
133 | { NULL, 0, NULL, 0 }, |
134 | }; |
135 | |
136 | int c = -1; |
137 | int err = 0; |
138 | int ver = 0; |
139 | int help = 0; |
140 | int levels = 0; |
141 | char *endserial; |
142 | |
143 | opterr = 0; /* We'll handle the errors, thank you. */ |
144 | |
145 | while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { |
146 | switch (c) { |
147 | case 1: /* A non-option argument */ |
148 | if (!err && !opts.device) |
149 | opts.device = argv[optind-1]; |
150 | else if (!err && !opts.label) |
151 | opts.label = argv[optind-1]; |
152 | else |
153 | err++; |
154 | break; |
155 | case 'f': |
156 | opts.force++; |
157 | break; |
158 | case 'h': |
159 | case '?': |
160 | if (strncmp (argv[optind-1], "--log-", 6) == 0) { |
161 | if (!ntfs_log_parse_option (argv[optind-1])) |
162 | err++; |
163 | break; |
164 | } |
165 | help++; |
166 | break; |
167 | case 'I' : /* not proposed as a short option letter */ |
168 | if (optarg) { |
169 | opts.serial = strtoll(optarg, &endserial, 16); |
170 | if (*endserial) |
171 | ntfs_log_error("Bad hexadecimal serial number.\n"); |
172 | } |
173 | opts.new_serial |= 2; |
174 | break; |
175 | case 'i' : /* not proposed as a short option letter */ |
176 | if (optarg) { |
177 | opts.serial = strtoll(optarg, &endserial, 16) |
178 | << 32; |
179 | if (*endserial) |
180 | ntfs_log_error("Bad hexadecimal serial number.\n"); |
181 | } |
182 | opts.new_serial |= 1; |
183 | break; |
184 | case 'n': |
185 | opts.noaction++; |
186 | break; |
187 | case 'q': |
188 | opts.quiet++; |
189 | ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); |
190 | break; |
191 | case 'v': |
192 | opts.verbose++; |
193 | ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); |
194 | break; |
195 | case 'V': |
196 | ver++; |
197 | break; |
198 | default: |
199 | ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); |
200 | err++; |
201 | break; |
202 | } |
203 | } |
204 | |
205 | /* Make sure we're in sync with the log levels */ |
206 | levels = ntfs_log_get_levels(); |
207 | if (levels & NTFS_LOG_LEVEL_VERBOSE) |
208 | opts.verbose++; |
209 | if (!(levels & NTFS_LOG_LEVEL_QUIET)) |
210 | opts.quiet++; |
211 | |
212 | if (help || ver) { |
213 | opts.quiet = 0; |
214 | } else { |
215 | if (opts.device == NULL) { |
216 | if (argc > 1) |
217 | ntfs_log_error("You must specify a device.\n"); |
218 | err++; |
219 | } |
220 | |
221 | if (opts.quiet && opts.verbose) { |
222 | ntfs_log_error("You may not use --quiet and --verbose at " |
223 | "the same time.\n"); |
224 | err++; |
225 | } |
226 | } |
227 | |
228 | if (ver) |
229 | version(); |
230 | if (help || err) |
231 | usage(); |
232 | |
233 | return (!err && !help && !ver); |
234 | } |
235 | |
236 | static int change_serial(ntfs_volume *vol, u64 sector, le64 serial_number, |
237 | NTFS_BOOT_SECTOR *bs, NTFS_BOOT_SECTOR *oldbs) |
238 | { |
239 | int res; |
240 | le64 mask; |
241 | BOOL same; |
242 | |
243 | res = -1; |
244 | if ((ntfs_pread(vol->dev, sector << vol->sector_size_bits, |
245 | vol->sector_size, bs) == vol->sector_size)) { |
246 | same = TRUE; |
247 | if (!sector) |
248 | /* save the real bootsector */ |
249 | memcpy(oldbs, bs, vol->sector_size); |
250 | else |
251 | /* backup bootsector must be similar */ |
252 | same = !memcmp(oldbs, bs, vol->sector_size); |
253 | if (same) { |
254 | if (opts.new_serial & 2) |
255 | bs->volume_serial_number = serial_number; |
256 | else { |
257 | mask = const_cpu_to_le64(~0x0ffffffffULL); |
258 | bs->volume_serial_number |
259 | = (serial_number & mask) |
260 | | (bs->volume_serial_number & ~mask); |
261 | } |
262 | if (opts.noaction |
263 | || (ntfs_pwrite(vol->dev, |
264 | sector << vol->sector_size_bits, |
265 | vol->sector_size, bs) == vol->sector_size)) { |
266 | res = 0; |
267 | } |
268 | } else { |
269 | ntfs_log_info("* Warning : the backup boot sector" |
270 | " does not match (leaving unchanged)\n"); |
271 | res = 0; |
272 | } |
273 | } |
274 | return (res); |
275 | } |
276 | |
277 | static int set_new_serial(ntfs_volume *vol) |
278 | { |
279 | NTFS_BOOT_SECTOR *bs; /* full boot sectors */ |
280 | NTFS_BOOT_SECTOR *oldbs; /* full original boot sector */ |
281 | le64 serial_number; |
282 | u64 number_of_sectors; |
283 | u64 sn; |
284 | int res; |
285 | |
286 | res = -1; |
287 | bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); |
288 | oldbs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); |
289 | if (bs && oldbs) { |
290 | if (opts.serial) |
291 | serial_number = cpu_to_le64(opts.serial); |
292 | else { |
293 | /* different values for parallel processes */ |
294 | srandom(time((time_t*)NULL) ^ (getpid() << 16)); |
295 | sn = ((u64)random() << 32) |
296 | | ((u64)random() & 0xffffffff); |
297 | serial_number = cpu_to_le64(sn); |
298 | } |
299 | if (!change_serial(vol, 0, serial_number, bs, oldbs)) { |
300 | number_of_sectors = le64_to_cpu(bs->number_of_sectors); |
301 | if (!change_serial(vol, number_of_sectors, |
302 | serial_number, bs, oldbs)) { |
303 | ntfs_log_info("New serial number : %016llx\n", |
304 | (long long)le64_to_cpu( |
305 | bs->volume_serial_number)); |
306 | res = 0; |
307 | } |
308 | } |
309 | free(bs); |
310 | free(oldbs); |
311 | } |
312 | if (res) |
313 | ntfs_log_info("Error setting a new serial number\n"); |
314 | return (res); |
315 | } |
316 | |
317 | static int print_serial(ntfs_volume *vol) |
318 | { |
319 | NTFS_BOOT_SECTOR *bs; /* full boot sectors */ |
320 | int res; |
321 | |
322 | res = -1; |
323 | bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); |
324 | if (bs |
325 | && (ntfs_pread(vol->dev, 0, |
326 | vol->sector_size, bs) == vol->sector_size)) { |
327 | ntfs_log_info("Serial number : %016llx\n", |
328 | (long long)le64_to_cpu(bs->volume_serial_number)); |
329 | res = 0; |
330 | free(bs); |
331 | } |
332 | if (res) |
333 | ntfs_log_info("Error getting the serial number\n"); |
334 | return (res); |
335 | } |
336 | |
337 | /** |
338 | * print_label - display the current label of a mounted ntfs partition. |
339 | * @dev: device to read the label from |
340 | * @mnt_flags: mount flags of the device or 0 if not mounted |
341 | * @mnt_point: mount point of the device or NULL |
342 | * |
343 | * Print the label of the device @dev. |
344 | */ |
345 | static int print_label(ntfs_volume *vol, unsigned long mnt_flags) |
346 | { |
347 | int result = 0; |
348 | //XXX significant? |
349 | if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) == |
350 | NTFS_MF_MOUNTED) { |
351 | ntfs_log_error("%s is mounted read-write, results may be " |
352 | "unreliable.\n", opts.device); |
353 | result = 1; |
354 | } |
355 | |
356 | if (opts.verbose) |
357 | ntfs_log_info("Volume label : %s\n", vol->vol_name); |
358 | else |
359 | ntfs_log_info("%s\n", vol->vol_name); |
360 | return result; |
361 | } |
362 | |
363 | /** |
364 | * change_label - change the current label on a device |
365 | * @dev: device to change the label on |
366 | * @mnt_flags: mount flags of the device or 0 if not mounted |
367 | * @mnt_point: mount point of the device or NULL |
368 | * @label: the new label |
369 | * |
370 | * Change the label on the device @dev to @label. |
371 | */ |
372 | static int change_label(ntfs_volume *vol, char *label) |
373 | { |
374 | ntfschar *new_label = NULL; |
375 | int label_len; |
376 | int result = 0; |
377 | |
378 | label_len = ntfs_mbstoucs(label, &new_label); |
379 | if (label_len == -1) { |
380 | ntfs_log_perror("Unable to convert label string to Unicode"); |
381 | return 1; |
382 | } |
383 | else if (label_len*sizeof(ntfschar) > 0x100) { |
384 | ntfs_log_warning("New label is too long. Maximum %u characters " |
385 | "allowed. Truncating %u excess characters.\n", |
386 | (unsigned)(0x100 / sizeof(ntfschar)), |
387 | (unsigned)(label_len - |
388 | (0x100 / sizeof(ntfschar)))); |
389 | label_len = 0x100 / sizeof(ntfschar); |
390 | label[label_len] = const_cpu_to_le16(0); |
391 | } |
392 | |
393 | if(!opts.noaction) |
394 | result = ntfs_volume_rename(vol, new_label, label_len) ? 1 : 0; |
395 | |
396 | free(new_label); |
397 | return result; |
398 | } |
399 | |
400 | /** |
401 | * main - Begin here |
402 | * |
403 | * Start from here. |
404 | * |
405 | * Return: 0 Success, the program worked |
406 | * 1 Error, something went wrong |
407 | */ |
408 | int main(int argc, char **argv) |
409 | { |
410 | unsigned long mnt_flags = 0; |
411 | int result = 0; |
412 | ntfs_volume *vol; |
413 | |
414 | ntfs_log_set_handler(ntfs_log_handler_outerr); |
415 | |
416 | if (!parse_options(argc, argv)) |
417 | return 1; |
418 | |
419 | utils_set_locale(); |
420 | |
421 | if ((opts.label || opts.new_serial) |
422 | && !opts.noaction |
423 | && !opts.force |
424 | && !ntfs_check_if_mounted(opts.device, &mnt_flags) |
425 | && (mnt_flags & NTFS_MF_MOUNTED)) { |
426 | ntfs_log_error("Cannot make changes to a mounted device\n"); |
427 | result = 1; |
428 | goto abort; |
429 | } |
430 | |
431 | if (!opts.label && !opts.new_serial) |
432 | opts.noaction++; |
433 | |
434 | vol = utils_mount_volume(opts.device, |
435 | (opts.noaction ? NTFS_MNT_RDONLY : 0) | |
436 | (opts.force ? NTFS_MNT_RECOVER : 0)); |
437 | if (!vol) |
438 | return 1; |
439 | |
440 | if (opts.new_serial) { |
441 | result = set_new_serial(vol); |
442 | if (result) |
443 | goto unmount; |
444 | } else { |
445 | if (opts.verbose) |
446 | result = print_serial(vol); |
447 | } |
448 | if (opts.label) |
449 | result = change_label(vol, opts.label); |
450 | else |
451 | result = print_label(vol, mnt_flags); |
452 | |
453 | unmount : |
454 | ntfs_umount(vol, FALSE); |
455 | abort : |
456 | return result; |
457 | } |
458 | |
459 |