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 *
8 * This utility will display/change the label on an NTFS partition.
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 2 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 (in the main directory of the Linux-NTFS
22 * distribution in the file COPYING); if not, write to the Free Software
23 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "config.h"
27
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #ifdef HAVE_STDIO_H
32 #include <stdio.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_ERRNO_H
38 #include <errno.h>
39 #endif
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43 #ifdef HAVE_GETOPT_H
44 #include <getopt.h>
45 #endif
46
47 #include "compat.h"
48 #include "debug.h"
49 #include "mft.h"
50 #include "utils.h"
51 #include "version.h"
52 #include "logging.h"
53
54 static const char *EXEC_NAME = "ntfslabel";
55
56 static struct options {
57 char *device; /* Device/File to work with */
58 char *label; /* Set the label to this */
59 int quiet; /* Less output */
60 int verbose; /* Extra output */
61 int force; /* Override common sense */
62 int noaction; /* Do not write to disk */
63 } opts;
64
65 /**
66 * version - Print version information about the program
67 *
68 * Print a copyright statement and a brief description of the program.
69 *
70 * Return: none
71 */
72 static void version(void)
73 {
74 ntfs_log_info("\n%s v%s (libntfs %s) - Display, or set, the label for an "
75 "NTFS Volume.\n\n", EXEC_NAME, VERSION,
76 ntfs_libntfs_version());
77 ntfs_log_info("Copyright (c)\n");
78 ntfs_log_info(" 2002 Matthew J. Fanto\n");
79 ntfs_log_info(" 2002-2005 Anton Altaparmakov\n");
80 ntfs_log_info(" 2002-2003 Richard Russon\n");
81 ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
82 }
83
84 /**
85 * usage - Print a list of the parameters to the program
86 *
87 * Print a list of the parameters and options for the program.
88 *
89 * Return: none
90 */
91 static void usage(void)
92 {
93 ntfs_log_info("\nUsage: %s [options] device [label]\n"
94 " -n, --no-action Do not write to disk\n"
95 " -f, --force Use less caution\n"
96 " -q, --quiet Less output\n"
97 " -v, --verbose More output\n"
98 " -V, --version Display version information\n"
99 " -h, --help Display this help\n\n",
100 EXEC_NAME);
101 ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
102 }
103
104 /**
105 * parse_options - Read and validate the programs command line
106 *
107 * Read the command line, verify the syntax and parse the options.
108 * This function is very long, but quite simple.
109 *
110 * Return: 1 Success
111 * 0 Error, one or more problems
112 */
113 static int parse_options(int argc, char *argv[])
114 {
115 static const char *sopt = "-fh?nqvV";
116 static const struct option lopt[] = {
117 { "force", no_argument, NULL, 'f' },
118 { "help", no_argument, NULL, 'h' },
119 { "no-action", no_argument, NULL, 'n' },
120 { "quiet", no_argument, NULL, 'q' },
121 { "verbose", no_argument, NULL, 'v' },
122 { "version", no_argument, NULL, 'V' },
123 { NULL, 0, NULL, 0 },
124 };
125
126 int c = -1;
127 int err = 0;
128 int ver = 0;
129 int help = 0;
130 int levels = 0;
131
132 opterr = 0; /* We'll handle the errors, thank you. */
133
134 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
135 switch (c) {
136 case 1: /* A non-option argument */
137 if (!err && !opts.device)
138 opts.device = argv[optind-1];
139 else if (!err && !opts.label)
140 opts.label = argv[optind-1];
141 else
142 err++;
143 break;
144 case 'f':
145 opts.force++;
146 break;
147 case 'h':
148 case '?':
149 if (strncmp (argv[optind-1], "--log-", 6) == 0) {
150 if (!ntfs_log_parse_option (argv[optind-1]))
151 err++;
152 break;
153 }
154 help++;
155 break;
156 case 'n':
157 opts.noaction++;
158 break;
159 case 'q':
160 opts.quiet++;
161 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
162 break;
163 case 'v':
164 opts.verbose++;
165 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
166 break;
167 case 'V':
168 ver++;
169 break;
170 default:
171 ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
172 err++;
173 break;
174 }
175 }
176
177 /* Make sure we're in sync with the log levels */
178 levels = ntfs_log_get_levels();
179 if (levels & NTFS_LOG_LEVEL_VERBOSE)
180 opts.verbose++;
181 if (!(levels & NTFS_LOG_LEVEL_QUIET))
182 opts.quiet++;
183
184 if (help || ver) {
185 opts.quiet = 0;
186 } else {
187 if (opts.device == NULL) {
188 if (argc > 1)
189 ntfs_log_error("You must specify a device.\n");
190 err++;
191 }
192
193 if (opts.quiet && opts.verbose) {
194 ntfs_log_error("You may not use --quiet and --verbose at "
195 "the same time.\n");
196 err++;
197 }
198 }
199
200 if (ver)
201 version();
202 if (help || err)
203 usage();
204
205 return (!err && !help && !ver);
206 }
207
208
209 /**
210 * print_label - display the current label of a mounted ntfs partition.
211 * @dev: device to read the label from
212 * @mnt_flags: mount flags of the device or 0 if not mounted
213 * @mnt_point: mount point of the device or NULL
214 *
215 * Print the label of the device @dev.
216 */
217 static int print_label(ntfs_volume *vol, unsigned long mnt_flags)
218 {
219 int result = 0;
220 //XXX significant?
221 if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) ==
222 NTFS_MF_MOUNTED) {
223 ntfs_log_error("%s is mounted read-write, results may be "
224 "unreliable.\n", opts.device);
225 result = 1;
226 }
227
228 ntfs_log_info("%s\n", vol->vol_name);
229 return result;
230 }
231
232 /**
233 * resize_resident_attribute_value - resize a resident attribute
234 * @m: mft record containing attribute to resize
235 * @a: attribute record (inside @m) which to resize
236 * @new_vsize: the new attribute value size to resize the attribute to
237 *
238 * Return 0 on success and -1 with errno = ENOSPC if not enough space in the
239 * mft record.
240 */
241 static int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a,
242 const u32 new_vsize)
243 {
244 int new_alen, new_muse;
245
246 /* New attribute length and mft record bytes used. */
247 new_alen = (le16_to_cpu(a->u.res.value_offset) + new_vsize + 7) & ~7;
248 new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) +
249 new_alen;
250 /* Check for sufficient space. */
251 if ((u32)new_muse > le32_to_cpu(m->bytes_allocated)) {
252 errno = ENOSPC;
253 return -1;
254 }
255 /* Move attributes behind @a to their new location. */
256 memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length),
257 le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) -
258 le32_to_cpu(a->length));
259 /* Adjust @m to reflect change in used space. */
260 m->bytes_in_use = cpu_to_le32(new_muse);
261 /* Adjust @a to reflect new value size. */
262 a->length = cpu_to_le32(new_alen);
263 a->u.res.value_length = cpu_to_le32(new_vsize);
264 return 0;
265 }
266
267 /**
268 * change_label - change the current label on a device
269 * @dev: device to change the label on
270 * @mnt_flags: mount flags of the device or 0 if not mounted
271 * @mnt_point: mount point of the device or NULL
272 * @label: the new label
273 *
274 * Change the label on the device @dev to @label.
275 */
276 static int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL force)
277 {
278 ntfs_attr_search_ctx *ctx;
279 ntfschar *new_label = NULL;
280 ATTR_RECORD *a;
281 int label_len;
282 int result = 0;
283
284 //XXX significant?
285 if (mnt_flags & NTFS_MF_MOUNTED) {
286 /* If not the root fs or mounted read/write, refuse change. */
287 if (!(mnt_flags & NTFS_MF_ISROOT) ||
288 !(mnt_flags & NTFS_MF_READONLY)) {
289 if (!force) {
290 ntfs_log_error("Refusing to change label on "
291 "read-%s mounted device %s.\n",
292 mnt_flags & NTFS_MF_READONLY ?
293 "only" : "write", opts.device);
294 return 1;
295 }
296 }
297 }
298 ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
299 if (!ctx) {
300 ntfs_log_perror("Failed to get attribute search context");
301 goto err_out;
302 }
303 if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0,
304 ctx)) {
305 if (errno != ENOENT) {
306 ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed");
307 goto err_out;
308 }
309 /* The volume name attribute does not exist. Need to add it. */
310 a = NULL;
311 } else {
312 a = ctx->attr;
313 if (a->non_resident) {
314 ntfs_log_error("Error: Attribute $VOLUME_NAME must be "
315 "resident.\n");
316 goto err_out;
317 }
318 }
319 label_len = ntfs_mbstoucs(label, &new_label, 0);
320 if (label_len == -1) {
321 ntfs_log_perror("Unable to convert label string to Unicode");
322 goto err_out;
323 }
324 label_len *= sizeof(ntfschar);
325 if (label_len > 0x100) {
326 ntfs_log_error("New label is too long. Maximum %u characters "
327 "allowed. Truncating excess characters.\n",
328 (unsigned)(0x100 / sizeof(ntfschar)));
329 label_len = 0x100;
330 new_label[label_len / sizeof(ntfschar)] = 0;
331 }
332 if (a) {
333 if (resize_resident_attribute_value(ctx->mrec, a, label_len)) {
334 ntfs_log_perror("Error resizing resident attribute");
335 goto err_out;
336 }
337 } else {
338 /* sizeof(resident attribute record header) == 24 */
339 int asize = (24 + label_len + 7) & ~7;
340 u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use);
341 if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) {
342 errno = ENOSPC;
343 ntfs_log_perror("Error adding resident attribute");
344 goto err_out;
345 }
346 a = ctx->attr;
347 memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec));
348 ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize);
349 a->type = AT_VOLUME_NAME;
350 a->length = cpu_to_le32(asize);
351 a->non_resident = 0;
352 a->name_length = 0;
353 a->name_offset = cpu_to_le16(24);
354 a->flags = cpu_to_le16(0);
355 a->instance = ctx->mrec->next_attr_instance;
356 ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu(
357 ctx->mrec->next_attr_instance) + 1) & 0xffff);
358 a->u.res.value_length = cpu_to_le32(label_len);
359 a->u.res.value_offset = a->name_offset;
360 a->u.res.resident_flags = 0;
361 a->u.res.reservedR = 0;
362 }
363 memcpy((u8*)a + le16_to_cpu(a->u.res.value_offset), new_label, label_len);
364 if (!opts.noaction && ntfs_inode_sync(vol->vol_ni)) {
365 ntfs_log_perror("Error writing MFT Record to disk");
366 goto err_out;
367 }
368 result = 0;
369 err_out:
370 free(new_label);
371 return result;
372 }
373
374 /**
375 * main - Begin here
376 *
377 * Start from here.
378 *
379 * Return: 0 Success, the program worked
380 * 1 Error, something went wrong
381 */
382 int main(int argc, char **argv)
383 {
384 unsigned long mnt_flags = 0;
385 int result = 0;
386 ntfs_volume *vol;
387
388 ntfs_log_set_handler(ntfs_log_handler_outerr);
389
390 if (!parse_options(argc, argv))
391 return 1;
392
393 utils_set_locale();
394
395 if (!opts.label)
396 opts.noaction++;
397
398 vol = utils_mount_volume(opts.device,
399 (opts.noaction ? NTFS_MNT_RDONLY : 0) |
400 (opts.force ? NTFS_MNT_FORCE : 0));
401 if (!vol)
402 return 1;
403
404 if (opts.label)
405 result = change_label(vol, mnt_flags, opts.label, opts.force);
406 else
407 result = print_label(vol, mnt_flags);
408
409 ntfs_umount(vol, FALSE);
410 return result;
411 }
412