http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200309071734.tar.bz2
[ntfsprogs.git] / ntfsprogs / ntfslabel.c
1 /**
2  * ntfslabel - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2002 Matthew J. Fanto
5  * Copyright (c) 2002 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 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <locale.h>
33 #include <getopt.h>
34
35 #include "debug.h"
36 #include "mft.h"
37 #include "utils.h"
38
39 static const char *EXEC_NAME = "ntfslabel";
40
41 static struct options {
42         char    *device;        /* Device/File to work with */
43         char    *label;         /* Set the label to this */
44         int      quiet;         /* Less output */
45         int      verbose;       /* Extra output */
46         int      force;         /* Override common sense */
47         int      noaction;      /* Do not write to disk */
48 } opts;
49
50 GEN_PRINTF (Eprintf, stderr, NULL,          FALSE)
51 GEN_PRINTF (Vprintf, stdout, &opts.verbose, TRUE)
52 GEN_PRINTF (Qprintf, stdout, &opts.quiet,   FALSE)
53
54 /**
55  * version - Print version information about the program
56  *
57  * Print a copyright statement and a brief description of the program.
58  *
59  * Return:  none
60  */
61 void version (void)
62 {
63         printf ("\n%s v%s - Display, or set, the label for an NTFS Volume.\n\n",
64                 EXEC_NAME, VERSION);
65         printf ("Copyright (c)\n");
66         printf ("    2002      Matthew J. Fanto\n");
67         printf ("    2002      Anton Altaparmakov\n");
68         printf ("    2002-2003 Richard Russon\n");
69         printf ("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
70 }
71
72 /**
73  * usage - Print a list of the parameters to the program
74  *
75  * Print a list of the parameters and options for the program.
76  *
77  * Return:  none
78  */
79 void usage (void)
80 {
81         printf ("\nUsage: %s [options] device [label]\n"
82                "    -n    --no-action    Do not write to disk\n"
83                "    -f    --force        Use less caution\n"
84                "    -q    --quiet        Less output\n"
85                "    -v    --verbose      More output\n"
86                "    -V    --version      Display version information\n"
87                "    -h    --help         Display this help\n\n",
88                EXEC_NAME);
89         printf ("%s%s\n", ntfs_bugs, ntfs_home);
90 }
91
92 /**
93  * parse_options - Read and validate the programs command line
94  *
95  * Read the command line, verify the syntax and parse the options.
96  * This function is very long, but quite simple.
97  *
98  * Return:  1 Success
99  *          0 Error, one or more problems
100  */
101 int parse_options (int argc, char *argv[])
102 {
103         static const char *sopt = "-fh?nqvV";
104         static const struct option lopt[] = {
105                 { "force",       no_argument,           NULL, 'f' },
106                 { "help",        no_argument,           NULL, 'h' },
107                 { "no-action",   no_argument,           NULL, 'n' },
108                 { "quiet",       no_argument,           NULL, 'q' },
109                 { "verbose",     no_argument,           NULL, 'v' },
110                 { "version",     no_argument,           NULL, 'V' },
111                 { NULL, 0, NULL, 0 },
112         };
113
114         char c = -1;
115         int err  = 0;
116         int ver  = 0;
117         int help = 0;
118
119         opterr = 0; /* We'll handle the errors, thank you. */
120
121         while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) {
122                 switch (c) {
123                 case 1: /* A non-option argument */
124                         if (!err && !opts.device)
125                                 opts.device = argv[optind-1];
126                         else if (!err && !opts.label)
127                                 opts.label = argv[optind-1];
128                         else
129                                 err++;
130                         break;
131                 case 'f':
132                         opts.force++;
133                         break;
134                 case 'h':
135                 case '?':
136                         help++;
137                         break;
138                 case 'n':
139                         opts.noaction++;
140                         break;
141                 case 'q':
142                         opts.quiet++;
143                         break;
144                 case 'v':
145                         opts.verbose++;
146                         break;
147                 case 'V':
148                         ver++;
149                         break;
150                 default:
151                         Eprintf ("Unknown option '%s'.\n", argv[optind-1]);
152                         err++;
153                         break;
154                 }
155         }
156
157         if (help || ver) {
158                 opts.quiet = 0;
159         } else {
160                 if (opts.device == NULL) {
161                         if (argc > 1)
162                                 Eprintf ("You must specify a device.\n");
163                         err++;
164                 }
165
166                 if (opts.quiet && opts.verbose) {
167                         Eprintf ("You may not use --quiet and --verbose at the same time.\n");
168                         err++;
169                 }
170         }
171
172         if (ver)
173                 version();
174         if (help || err)
175                 usage();
176
177         return (!err && !help && !ver);
178 }
179
180
181 /**
182  * print_label - display the current label of a mounted ntfs partition.
183  * @dev:        device to read the label from
184  * @mnt_flags:  mount flags of the device or 0 if not mounted
185  * @mnt_point:  mount point of the device or NULL
186  *
187  * Print the label of the device @dev to stdout.
188  */
189 int print_label (ntfs_volume *vol, unsigned long mnt_flags)
190 {
191         int result = 0;
192         //XXX significant?
193         if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) ==
194                         NTFS_MF_MOUNTED) {
195                 Eprintf ("%s is mounted read-write, results may be "
196                         "unreliable.\n", opts.device);
197                 result = 1;
198         }
199
200         printf("%s\n", vol->vol_name);
201         return result;
202 }
203
204 /**
205  * resize_resident_attribute_value - resize a resident attribute
206  * @m:          mft record containing attribute to resize
207  * @a:          attribute record (inside @m) which to resize
208  * @new_vsize:  the new attribute value size to resize the attribute to
209  *
210  * Return 0 on success and -1 with errno = ENOSPC if not enough space in the
211  * mft record.
212  */
213 int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a,
214                 const u32 new_vsize)
215 {
216         int new_alen, new_muse;
217
218         /* New attribute length and mft record bytes used. */
219         new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7;
220         new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) +
221                         new_alen;
222         /* Check for sufficient space. */
223         if (new_muse > le32_to_cpu(m->bytes_allocated)) {
224                 errno = ENOSPC;
225                 return -1;
226         }
227         /* Move attributes behind @a to their new location. */
228         memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length),
229                         le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) -
230                         le32_to_cpu(a->length));
231         /* Adjust @m to reflect change in used space. */
232         m->bytes_in_use = cpu_to_le32(new_muse);
233         /* Adjust @a to reflect new value size. */
234         a->length = cpu_to_le32(new_alen);
235         a->value_length = cpu_to_le32(new_vsize);
236         return 0;
237 }
238
239 /**
240  * change_label - change the current label on a device
241  * @dev:        device to change the label on
242  * @mnt_flags:  mount flags of the device or 0 if not mounted
243  * @mnt_point:  mount point of the device or NULL
244  * @label:      the new label
245  *
246  * Change the label on the device @dev to @label.
247  */
248 int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL force)
249 {
250         ntfs_attr_search_ctx *ctx = NULL;
251         uchar_t *new_label = NULL;
252         MFT_RECORD *mrec = NULL;
253         ATTR_RECORD *a;
254         int label_len;
255         int result = 0;
256
257         //XXX significant?
258         if (mnt_flags & NTFS_MF_MOUNTED) {
259                 /* If not the root fs or mounted read/write, refuse change. */
260                 if (!(mnt_flags & NTFS_MF_ISROOT) ||
261                                 !(mnt_flags & NTFS_MF_READONLY)) {
262                         if (!force) {
263                                 fprintf(stderr, "Refusing to change label on "
264                                                 "read-%s mounted device %s.\n",
265                                                 mnt_flags & NTFS_MF_READONLY ?
266                                                 "only" : "write", opts.device);
267                                 return 1;
268                         }
269                 }
270         }
271
272         if (ntfs_file_record_read(vol, (MFT_REF)FILE_Volume, &mrec, NULL)) {
273                 perror("Error reading file record");
274                 goto err_out;
275         }
276         if (!(mrec->flags & MFT_RECORD_IN_USE)) {
277                 fprintf(stderr, "Error: $Volume has been deleted. Run "
278                                 "chkdsk to fix this.\n");
279                 goto err_out;
280         }
281         ctx = ntfs_attr_get_search_ctx(NULL, mrec);
282         if (!ctx) {
283                 perror("Failed to get attribute search context");
284                 goto err_out;
285         }
286         if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
287                 perror("Lookup of $VOLUME_NAME attribute failed");
288                 goto err_out;
289         }
290         a = ctx->attr;
291         if (a->non_resident) {
292                 fprintf(stderr, "Error: Attribute $VOLUME_NAME must be "
293                                 "resident.\n");
294                 goto err_out;
295         }
296         label_len = ntfs_mbstoucs(label, &new_label, 0);
297         if (label_len == -1) {
298                 perror("Unable to convert label string to Unicode");
299                 goto err_out;
300         }
301         label_len *= sizeof(uchar_t);
302         if (label_len > 0x100) {
303                 fprintf(stderr, "New label is too long. Maximum %i characters "
304                                 "allowed. Truncating excess characters.\n",
305                                 0x100 / sizeof(uchar_t));
306                 label_len = 0x100;
307                 new_label[label_len / sizeof(uchar_t)] = cpu_to_le16(L'\0');
308         }
309         if (resize_resident_attribute_value(mrec, a, label_len)) {
310                 perror("Error resizing resident attribute");
311                 goto err_out;
312         }
313         memcpy((char*)a + le16_to_cpu(a->value_offset), new_label, label_len);
314         if (ntfs_mft_record_write(vol, (MFT_REF)FILE_Volume, mrec)) {
315                 perror("Error writing MFT Record to disk");
316                 goto err_out;
317         }
318         result = 0;
319 err_out:
320         if (new_label)
321                 free(new_label);
322         if (mrec)
323                 free(mrec);
324         return result;
325 }
326
327 /**
328  * main - Begin here
329  *
330  * Start from here.
331  *
332  * Return:  0  Success, the program worked
333  *          1  Error, something went wrong
334  */
335 int main(int argc, char **argv)
336 {
337         unsigned long mnt_flags = 0;
338         int result = 0;
339         ntfs_volume *vol;
340
341         if (!parse_options (argc, argv))
342                 return 1;
343
344         utils_set_locale();
345
346         //XXX need to set and get mount flags
347         vol = utils_mount_volume (opts.device, 0, opts.force);
348         if (!vol)
349                 return 1;
350
351         if (opts.label)
352                 result = change_label (vol, mnt_flags, opts.label, opts.force);
353         else
354                 result = print_label (vol, mnt_flags);
355
356         ntfs_umount (vol, FALSE);
357         return result;
358 }
359