Whamcloud - gitweb
Merge branch 'maint'
[tools/e2fsprogs.git] / misc / tune2fs.c
1 /*
2  * tune2fs.c - Change the file system parameters on an ext2 file system
3  *
4  * Copyright (C) 1992, 1993, 1994  Remy Card <card@masi.ibp.fr>
5  *                                 Laboratoire MASI, Institut Blaise Pascal
6  *                                 Universite Pierre et Marie Curie (Paris VI)
7  *
8  * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
9  *
10  * %Begin-Header%
11  * This file may be redistributed under the terms of the GNU Public
12  * License.
13  * %End-Header%
14  */
15
16 /*
17  * History:
18  * 93/06/01     - Creation
19  * 93/10/31     - Added the -c option to change the maximal mount counts
20  * 93/12/14     - Added -l flag to list contents of superblock
21  *                M.J.E. Mol (marcel@duteca.et.tudelft.nl)
22  *                F.W. ten Wolde (franky@duteca.et.tudelft.nl)
23  * 93/12/29     - Added the -e option to change errors behavior
24  * 94/02/27     - Ported to use the ext2fs library
25  * 94/03/06     - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
26  */
27
28 #define _XOPEN_SOURCE 500 /* for inclusion of strptime() */
29 #define _BSD_SOURCE /* for inclusion of strcasecmp() */
30 #include <fcntl.h>
31 #include <grp.h>
32 #ifdef HAVE_GETOPT_H
33 #include <getopt.h>
34 #else
35 extern char *optarg;
36 extern int optind;
37 #endif
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45
46 #include "ext2fs/ext2_fs.h"
47 #include "ext2fs/ext2fs.h"
48 #include "et/com_err.h"
49 #include "uuid/uuid.h"
50 #include "e2p/e2p.h"
51 #include "jfs_user.h"
52 #include "util.h"
53 #include "blkid/blkid.h"
54
55 #include "../version.h"
56 #include "nls-enable.h"
57
58 /* 
59  * Tune2fs supports these features in addition to the standard features.
60  */
61 #define EXT2_TUNE2FS_INCOMPAT   (EXT3_FEATURE_INCOMPAT_EXTENTS)
62 #define EXT2_TUNE2FS_RO_COMPAT  (EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
63                                  EXT4_FEATURE_RO_COMPAT_GDT_CSUM|       \
64                                  EXT4_FEATURE_RO_COMPAT_DIR_NLINK|      \
65                                  EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)
66
67
68 const char * program_name = "tune2fs";
69 char * device_name;
70 char * new_label, *new_last_mounted, *new_UUID;
71 char * io_options;
72 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
73 static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
74 static time_t last_check_time;
75 static int print_label;
76 static int max_mount_count, mount_count, mount_flags;
77 static unsigned long interval, reserved_blocks;
78 static double reserved_ratio;
79 static unsigned long resgid, resuid;
80 static unsigned short errors;
81 static int open_flag;
82 static char *features_cmd;
83 static char *mntopts_cmd;
84 static int stride, stripe_width;
85 static int stride_set, stripe_width_set;
86 static char *extended_cmd;
87
88 int journal_size, journal_flags;
89 char *journal_device;
90
91 static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
92
93 void do_findfs(int argc, char **argv);
94
95 static void usage(void)
96 {
97         fprintf(stderr,
98                 _("Usage: %s [-c max_mounts_count] [-e errors_behavior] "
99                   "[-g group]\n"
100                   "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
101                   "\t[-m reserved_blocks_percent] "
102                   "[-o [^]mount_options[,...]] \n"
103                   "\t[-r reserved_blocks_count] [-u user] [-C mount_count] "
104                   "[-L volume_label]\n"
105                   "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n"
106                   "\t[-E extended-option[,...]] [-T last_check_time] "
107                   "[-U UUID] device\n"), program_name);
108         exit (1);
109 }
110
111 static __u32 ok_features[3] = {
112         /* Compat */
113         EXT3_FEATURE_COMPAT_HAS_JOURNAL |
114                 EXT2_FEATURE_COMPAT_DIR_INDEX,
115         /* Incompat */
116         EXT2_FEATURE_INCOMPAT_FILETYPE |
117                 EXT3_FEATURE_INCOMPAT_EXTENTS |
118                 EXT4_FEATURE_INCOMPAT_FLEX_BG,
119         /* R/O compat */
120         EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
121                 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
122 };
123
124 static __u32 clear_ok_features[3] = {
125         /* Compat */
126         EXT3_FEATURE_COMPAT_HAS_JOURNAL |
127                 EXT2_FEATURE_COMPAT_RESIZE_INODE |
128                 EXT2_FEATURE_COMPAT_DIR_INDEX,
129         /* Incompat */
130         EXT2_FEATURE_INCOMPAT_FILETYPE |
131                 EXT4_FEATURE_INCOMPAT_FLEX_BG,
132         /* R/O compat */
133         EXT2_FEATURE_RO_COMPAT_LARGE_FILE
134 };
135
136 /*
137  * Remove an external journal from the filesystem
138  */
139 static void remove_journal_device(ext2_filsys fs)
140 {
141         char            *journal_path;
142         ext2_filsys     jfs;
143         char            buf[1024];
144         journal_superblock_t    *jsb;
145         int             i, nr_users;
146         errcode_t       retval;
147         int             commit_remove_journal = 0;
148         io_manager      io_ptr;
149
150         if (f_flag)
151                 commit_remove_journal = 1; /* force removal even if error */
152
153         uuid_unparse(fs->super->s_journal_uuid, buf);
154         journal_path = blkid_get_devname(NULL, "UUID", buf);
155
156         if (!journal_path) {
157                 journal_path =
158                         ext2fs_find_block_device(fs->super->s_journal_dev);
159                 if (!journal_path)
160                         return;
161         }
162
163 #ifdef CONFIG_TESTIO_DEBUG
164         io_ptr = test_io_manager;
165         test_io_backing_manager = unix_io_manager;
166 #else
167         io_ptr = unix_io_manager;
168 #endif
169         retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
170                              EXT2_FLAG_JOURNAL_DEV_OK, 0,
171                              fs->blocksize, io_ptr, &jfs);
172         if (retval) {
173                 com_err(program_name, retval,
174                         _("while trying to open external journal"));
175                 goto no_valid_journal;
176         }
177         if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
178                 fprintf(stderr, _("%s is not a journal device.\n"),
179                         journal_path);
180                 goto no_valid_journal;
181         }
182
183         /* Get the journal superblock */
184         if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
185                 com_err(program_name, retval,
186                         _("while reading journal superblock"));
187                 goto no_valid_journal;
188         }
189
190         jsb = (journal_superblock_t *) buf;
191         if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
192             (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
193                 fputs(_("Journal superblock not found!\n"), stderr);
194                 goto no_valid_journal;
195         }
196
197         /* Find the filesystem UUID */
198         nr_users = ntohl(jsb->s_nr_users);
199         for (i=0; i < nr_users; i++) {
200                 if (memcmp(fs->super->s_uuid,
201                            &jsb->s_users[i*16], 16) == 0)
202                         break;
203         }
204         if (i >= nr_users) {
205                 fputs(_("Filesystem's UUID not found on journal device.\n"), 
206                       stderr);
207                 commit_remove_journal = 1;
208                 goto no_valid_journal;
209         }
210         nr_users--;
211         for (i=0; i < nr_users; i++)
212                 memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
213         jsb->s_nr_users = htonl(nr_users);
214
215         /* Write back the journal superblock */
216         if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
217                 com_err(program_name, retval,
218                         "while writing journal superblock.");
219                 goto no_valid_journal;
220         }
221
222         commit_remove_journal = 1;
223
224 no_valid_journal:
225         if (commit_remove_journal == 0) {
226                 fputs(_("Journal NOT removed\n"), stderr);
227                 exit(1);
228         }
229         fs->super->s_journal_dev = 0;
230         uuid_clear(fs->super->s_journal_uuid);
231         ext2fs_mark_super_dirty(fs);
232         fputs(_("Journal removed\n"), stdout);
233         free(journal_path);
234 }
235
236 /* Helper function for remove_journal_inode */
237 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
238                                int blockcnt EXT2FS_ATTR((unused)), 
239                                void *private EXT2FS_ATTR((unused)))
240 {
241         blk_t   block;
242         int     group;
243
244         block = *blocknr;
245         ext2fs_unmark_block_bitmap(fs->block_map,block);
246         group = ext2fs_group_of_blk(fs, block);
247         fs->group_desc[group].bg_free_blocks_count++;
248         fs->super->s_free_blocks_count++;
249         return 0;
250 }
251
252 /*
253  * Remove the journal inode from the filesystem
254  */
255 static void remove_journal_inode(ext2_filsys fs)
256 {
257         struct ext2_inode       inode;
258         errcode_t               retval;
259         ino_t                   ino = fs->super->s_journal_inum;
260         
261         retval = ext2fs_read_inode(fs, ino,  &inode);
262         if (retval) {
263                 com_err(program_name, retval,
264                         _("while reading journal inode"));
265                 exit(1);
266         }
267         if (ino == EXT2_JOURNAL_INO) {
268                 retval = ext2fs_read_bitmaps(fs);
269                 if (retval) {
270                         com_err(program_name, retval,
271                                 _("while reading bitmaps"));
272                         exit(1);
273                 }
274                 retval = ext2fs_block_iterate(fs, ino,
275                                               BLOCK_FLAG_READ_ONLY, NULL,
276                                               release_blocks_proc, NULL);
277                 if (retval) {
278                         com_err(program_name, retval,
279                                 _("while clearing journal inode"));
280                         exit(1);
281                 }
282                 memset(&inode, 0, sizeof(inode));
283                 ext2fs_mark_bb_dirty(fs);
284                 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
285         } else
286                 inode.i_flags &= ~EXT2_IMMUTABLE_FL;
287         retval = ext2fs_write_inode(fs, ino, &inode);
288         if (retval) {
289                 com_err(program_name, retval,
290                         _("while writing journal inode"));
291                 exit(1);
292         }
293         fs->super->s_journal_inum = 0;
294         ext2fs_mark_super_dirty(fs);
295 }
296
297 /*
298  * Update the default mount options
299  */
300 static void update_mntopts(ext2_filsys fs, char *mntopts)
301 {
302         struct ext2_super_block *sb= fs->super;
303
304         if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
305                 fprintf(stderr, _("Invalid mount option set: %s\n"),
306                         mntopts);
307                 exit(1);
308         }
309         ext2fs_mark_super_dirty(fs);
310 }
311
312 /*
313  * Update the feature set as provided by the user.
314  */
315 static void update_feature_set(ext2_filsys fs, char *features)
316 {
317         struct ext2_super_block *sb= fs->super;
318         __u32   old_compat, old_incompat, old_ro_compat;
319         __u32           old_features[3];
320         int             type_err;
321         unsigned int    mask_err;
322
323 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
324                                 ((&sb->s_feature_compat)[(type)] & (mask)))
325 #define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
326                                  !((&sb->s_feature_compat)[(type)] & (mask)))
327 #define FEATURE_CHANGED(type, mask) ((mask) & \
328                      (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
329
330         old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
331         old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
332         old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
333
334         if (e2p_edit_feature2(features, &sb->s_feature_compat,
335                               ok_features, clear_ok_features,
336                               &type_err, &mask_err)) {
337                 if (!mask_err)
338                         fprintf(stderr,
339                                 _("Invalid filesystem option set: %s\n"),
340                                 features);
341                 else if (type_err & E2P_FEATURE_NEGATE_FLAG)
342                         fprintf(stderr, _("Clearing filesystem feature '%s' "
343                                           "not supported.\n"),
344                                 e2p_feature2string(type_err &
345                                                    E2P_FEATURE_TYPE_MASK,
346                                                    mask_err));
347                 else
348                         fprintf(stderr, _("Setting filesystem feature '%s' "
349                                           "not supported.\n"),
350                                 e2p_feature2string(type_err, mask_err));
351                 exit(1);
352         }
353
354         if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
355                 if ((mount_flags & EXT2_MF_MOUNTED) &&
356                     !(mount_flags & EXT2_MF_READONLY)) {
357                         fputs(_("The has_journal flag may only be "
358                                 "cleared when the filesystem is\n"
359                                 "unmounted or mounted "
360                                 "read-only.\n"), stderr);
361                         exit(1);
362                 }
363                 if (sb->s_feature_incompat &
364                     EXT3_FEATURE_INCOMPAT_RECOVER) {
365                         fputs(_("The needs_recovery flag is set.  "
366                                 "Please run e2fsck before clearing\n"
367                                 "the has_journal flag.\n"), stderr);
368                         exit(1);
369                 }
370                 if (sb->s_journal_inum) {
371                         remove_journal_inode(fs);
372                 }
373                 if (sb->s_journal_dev) {
374                         remove_journal_device(fs);
375                 }
376         }
377
378         if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
379                 /*
380                  * If adding a journal flag, let the create journal
381                  * code below handle creating setting the flag and
382                  * creating the journal.  We supply a default size if
383                  * necessary.
384                  */
385                 if (!journal_size)
386                         journal_size = -1;
387                 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
388         }
389
390         if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
391                 if (!sb->s_def_hash_version)
392                         sb->s_def_hash_version = EXT2_HASH_TEA;
393                 if (uuid_is_null((unsigned char *) sb->s_hash_seed))
394                         uuid_generate((unsigned char *) sb->s_hash_seed);
395         }
396
397         if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
398                 if (ext2fs_check_desc(fs)) {
399                         fputs(_("Clearing the flex_bg flag would "
400                                 "cause the the filesystem to be\n"
401                                 "inconsistent.\n"), stderr);
402                         exit(1);
403                 }
404         }
405
406         if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
407             (sb->s_feature_compat || sb->s_feature_ro_compat ||
408              sb->s_feature_incompat))
409                 ext2fs_update_dynamic_rev(fs);
410
411         if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
412                             EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
413             FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
414                             EXT2_FEATURE_INCOMPAT_FILETYPE) ||
415             FEATURE_CHANGED(E2P_FEATURE_COMPAT,
416                             EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
417             FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
418                         EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
419                 sb->s_state &= ~EXT2_VALID_FS;
420                 printf("\n%s\n", _(please_fsck));
421         }
422
423         if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
424             (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) ||
425             (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat))
426                 ext2fs_mark_super_dirty(fs);
427 }
428
429 /*
430  * Add a journal to the filesystem.
431  */
432 static void add_journal(ext2_filsys fs)
433 {
434         unsigned long journal_blocks;
435         errcode_t       retval;
436         ext2_filsys     jfs;
437         io_manager      io_ptr;
438
439         if (fs->super->s_feature_compat &
440             EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
441                 fputs(_("The filesystem already has a journal.\n"), stderr);
442                 goto err;
443         }
444         if (journal_device) {
445                 check_plausibility(journal_device);
446                 check_mount(journal_device, 0, _("journal"));
447 #ifdef CONFIG_TESTIO_DEBUG
448                 io_ptr = test_io_manager;
449                 test_io_backing_manager = unix_io_manager;
450 #else
451                 io_ptr = unix_io_manager;
452 #endif
453                 retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
454                                      EXT2_FLAG_JOURNAL_DEV_OK, 0,
455                                      fs->blocksize, io_ptr, &jfs);
456                 if (retval) {
457                         com_err(program_name, retval,
458                                 _("\n\twhile trying to open journal on %s\n"),
459                                 journal_device);
460                         goto err;
461                 }
462                 printf(_("Creating journal on device %s: "),
463                        journal_device);
464                 fflush(stdout);
465
466                 retval = ext2fs_add_journal_device(fs, jfs);
467                 ext2fs_close(jfs);
468                 if (retval) {
469                         com_err (program_name, retval,
470                                  _("while adding filesystem to journal on %s"),
471                                  journal_device);
472                         goto err;
473                 }
474                 fputs(_("done\n"), stdout);
475         } else if (journal_size) {
476                 fputs(_("Creating journal inode: "), stdout);
477                 fflush(stdout);
478                 journal_blocks = figure_journal_size(journal_size, fs);
479
480                 retval = ext2fs_add_journal_inode(fs, journal_blocks,
481                                                   journal_flags);
482                 if (retval) {
483                         fprintf(stderr, "\n");
484                         com_err(program_name, retval,
485                                 _("\n\twhile trying to create journal file"));
486                         exit(1);
487                 } else
488                         fputs(_("done\n"), stdout);
489                 /*
490                  * If the filesystem wasn't mounted, we need to force
491                  * the block group descriptors out.
492                  */
493                 if ((mount_flags & EXT2_MF_MOUNTED) == 0)
494                         fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
495         }
496         print_check_message(fs);
497         return;
498
499 err:
500         if (journal_device)
501                 free(journal_device);
502         exit(1);
503 }
504
505
506 static void parse_e2label_options(int argc, char ** argv)
507 {
508         if ((argc < 2) || (argc > 3)) {
509                 fputs(_("Usage: e2label device [newlabel]\n"), stderr);
510                 exit(1);
511         }
512         io_options = strchr(argv[1], '?');
513         if (io_options)
514                 *io_options++ = 0;
515         device_name = blkid_get_devname(NULL, argv[1], NULL);
516         if (!device_name) {
517                 com_err("e2label", 0, _("Unable to resolve '%s'"), 
518                         argv[1]);
519                 exit(1);
520         }
521         open_flag = EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_JOURNAL_DEV_OK;
522         if (argc == 3) {
523                 open_flag |= EXT2_FLAG_RW;
524                 L_flag = 1;
525                 new_label = argv[2];
526         } else 
527                 print_label++;
528 }
529
530 static time_t parse_time(char *str)
531 {
532         struct  tm      ts;
533
534         if (strcmp(str, "now") == 0) {
535                 return (time(0));
536         }
537         memset(&ts, 0, sizeof(ts));
538 #ifdef HAVE_STRPTIME
539         strptime(str, "%Y%m%d%H%M%S", &ts);
540 #else
541         sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
542                &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
543         ts.tm_year -= 1900;
544         ts.tm_mon -= 1;
545         if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
546             ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
547             ts.tm_min > 59 || ts.tm_sec > 61)
548                 ts.tm_mday = 0;
549 #endif
550         if (ts.tm_mday == 0) {
551                 com_err(program_name, 0,
552                         _("Couldn't parse date/time specifier: %s"),
553                         str);
554                 usage();
555         }
556         return (mktime(&ts));
557 }
558
559 static void parse_tune2fs_options(int argc, char **argv)
560 {
561         int c;
562         char * tmp;
563         struct group * gr;
564         struct passwd * pw;
565
566         open_flag = EXT2_FLAG_SOFTSUPP_FEATURES;
567
568         printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
569         while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:J:L:M:O:T:U:")) != EOF)
570                 switch (c)
571                 {
572                         case 'c':
573                                 max_mount_count = strtol (optarg, &tmp, 0);
574                                 if (*tmp || max_mount_count > 16000) {
575                                         com_err (program_name, 0,
576                                                  _("bad mounts count - %s"),
577                                                  optarg);
578                                         usage();
579                                 }
580                                 if (max_mount_count == 0)
581                                         max_mount_count = -1;
582                                 c_flag = 1;
583                                 open_flag = EXT2_FLAG_RW;
584                                 break;
585                         case 'C':
586                                 mount_count = strtoul (optarg, &tmp, 0);
587                                 if (*tmp || mount_count > 16000) {
588                                         com_err (program_name, 0,
589                                                  _("bad mounts count - %s"),
590                                                  optarg);
591                                         usage();
592                                 }
593                                 C_flag = 1;
594                                 open_flag = EXT2_FLAG_RW;
595                                 break;
596                         case 'e':
597                                 if (strcmp (optarg, "continue") == 0)
598                                         errors = EXT2_ERRORS_CONTINUE;
599                                 else if (strcmp (optarg, "remount-ro") == 0)
600                                         errors = EXT2_ERRORS_RO;
601                                 else if (strcmp (optarg, "panic") == 0)
602                                         errors = EXT2_ERRORS_PANIC;
603                                 else {
604                                         com_err (program_name, 0,
605                                                  _("bad error behavior - %s"),
606                                                  optarg);
607                                         usage();
608                                 }
609                                 e_flag = 1;
610                                 open_flag = EXT2_FLAG_RW;
611                                 break;
612                         case 'E':
613                                 extended_cmd = optarg;
614                                 open_flag |= EXT2_FLAG_RW;
615                                 break;
616                         case 'f': /* Force */
617                                 f_flag = 1;
618                                 break;
619                         case 'g':
620                                 resgid = strtoul (optarg, &tmp, 0);
621                                 if (*tmp) {
622                                         gr = getgrnam (optarg);
623                                         if (gr == NULL)
624                                                 tmp = optarg;
625                                         else {
626                                                 resgid = gr->gr_gid;
627                                                 *tmp =0;
628                                         }
629                                 }
630                                 if (*tmp) {
631                                         com_err (program_name, 0,
632                                                  _("bad gid/group name - %s"),
633                                                  optarg);
634                                         usage();
635                                 }
636                                 g_flag = 1;
637                                 open_flag = EXT2_FLAG_RW;
638                                 break;
639                         case 'i':
640                                 interval = strtoul (optarg, &tmp, 0);
641                                 switch (*tmp) {
642                                 case 's':
643                                         tmp++;
644                                         break;
645                                 case '\0':
646                                 case 'd':
647                                 case 'D': /* days */
648                                         interval *= 86400;
649                                         if (*tmp != '\0')
650                                                 tmp++;
651                                         break;
652                                 case 'm':
653                                 case 'M': /* months! */
654                                         interval *= 86400 * 30;
655                                         tmp++;
656                                         break;
657                                 case 'w':
658                                 case 'W': /* weeks */
659                                         interval *= 86400 * 7;
660                                         tmp++;
661                                         break;
662                                 }
663                                 if (*tmp) {
664                                         com_err (program_name, 0,
665                                                 _("bad interval - %s"), optarg);
666                                         usage();
667                                 }
668                                 i_flag = 1;
669                                 open_flag = EXT2_FLAG_RW;
670                                 break;
671                         case 'j':
672                                 if (!journal_size)
673                                         journal_size = -1;
674                                 open_flag = EXT2_FLAG_RW;
675                                 break;
676                         case 'J':
677                                 parse_journal_opts(optarg);
678                                 open_flag = EXT2_FLAG_RW;
679                                 break;
680                         case 'l':
681                                 l_flag = 1;
682                                 break;
683                         case 'L':
684                                 new_label = optarg;
685                                 L_flag = 1;
686                                 open_flag |= EXT2_FLAG_RW |
687                                         EXT2_FLAG_JOURNAL_DEV_OK;
688                                 break;
689                         case 'm':
690                                 reserved_ratio = strtod(optarg, &tmp);
691                                 if (*tmp || reserved_ratio > 50) {
692                                         com_err (program_name, 0,
693                                                  _("bad reserved block ratio - %s"),
694                                                  optarg);
695                                         usage();
696                                 }
697                                 m_flag = 1;
698                                 open_flag = EXT2_FLAG_RW;
699                                 break;
700                         case 'M':
701                                 new_last_mounted = optarg;
702                                 M_flag = 1;
703                                 open_flag = EXT2_FLAG_RW;
704                                 break;
705                         case 'o':
706                                 if (mntopts_cmd) {
707                                         com_err (program_name, 0,
708                                          _("-o may only be specified once"));
709                                         usage();
710                                 }
711                                 mntopts_cmd = optarg;
712                                 open_flag = EXT2_FLAG_RW;
713                                 break;
714                                 
715                         case 'O':
716                                 if (features_cmd) {
717                                         com_err (program_name, 0,
718                                          _("-O may only be specified once"));
719                                         usage();
720                                 }
721                                 features_cmd = optarg;
722                                 open_flag = EXT2_FLAG_RW;
723                                 break;
724                         case 'r':
725                                 reserved_blocks = strtoul (optarg, &tmp, 0);
726                                 if (*tmp) {
727                                         com_err (program_name, 0,
728                                                  _("bad reserved blocks count - %s"),
729                                                  optarg);
730                                         usage();
731                                 }
732                                 r_flag = 1;
733                                 open_flag = EXT2_FLAG_RW;
734                                 break;
735                         case 's': /* Deprecated */
736                                 s_flag = atoi(optarg);
737                                 open_flag = EXT2_FLAG_RW;
738                                 break;
739                         case 'T':
740                                 T_flag = 1;
741                                 last_check_time = parse_time(optarg);
742                                 open_flag = EXT2_FLAG_RW;
743                                 break;
744                         case 'u':
745                                 resuid = strtoul (optarg, &tmp, 0);
746                                 if (*tmp) {
747                                         pw = getpwnam (optarg);
748                                         if (pw == NULL)
749                                                 tmp = optarg;
750                                         else {
751                                                 resuid = pw->pw_uid;
752                                                 *tmp = 0;
753                                         }
754                                 }
755                                 if (*tmp) {
756                                         com_err (program_name, 0,
757                                                  _("bad uid/user name - %s"),
758                                                  optarg);
759                                         usage();
760                                 }
761                                 u_flag = 1;
762                                 open_flag = EXT2_FLAG_RW;
763                                 break;
764                         case 'U':
765                                 new_UUID = optarg;
766                                 U_flag = 1;
767                                 open_flag = EXT2_FLAG_RW |
768                                         EXT2_FLAG_JOURNAL_DEV_OK;
769                                 break;
770                         default:
771                                 usage();
772                 }
773         if (optind < argc - 1 || optind == argc)
774                 usage();
775         if (!open_flag && !l_flag)
776                 usage();
777         io_options = strchr(argv[optind], '?');
778         if (io_options)
779                 *io_options++ = 0;
780         device_name = blkid_get_devname(NULL, argv[optind], NULL);
781         if (!device_name) {
782                 com_err("tune2fs", 0, _("Unable to resolve '%s'"), 
783                         argv[optind]);
784                 exit(1);
785         }
786 }
787
788 void do_findfs(int argc, char **argv)
789 {
790         char    *dev;
791
792         if ((argc != 2) ||
793             (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
794                 fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
795                 exit(2);
796         }
797         dev = blkid_get_devname(NULL, argv[1], NULL);
798         if (!dev) {
799                 com_err("findfs", 0, _("Unable to resolve '%s'"), 
800                         argv[1]);
801                 exit(1);
802         }
803         puts(dev);
804         exit(0);
805 }
806
807 /*
808  * Note!  If any extended options are incompatible with the
809  * intersection of the SOFTSUPP features and those features explicitly
810  * enabled for tune2fs, there needs to be an explicit test for them
811  * here.
812  */
813 static void parse_extended_opts(ext2_filsys fs, const char *opts)
814 {
815         char    *buf, *token, *next, *p, *arg;
816         int     len;
817         int     r_usage = 0;
818
819         len = strlen(opts);
820         buf = malloc(len+1);
821         if (!buf) {
822                 fprintf(stderr,
823                         _("Couldn't allocate memory to parse options!\n"));
824                 exit(1);
825         }
826         strcpy(buf, opts);
827         for (token = buf; token && *token; token = next) {
828                 p = strchr(token, ',');
829                 next = 0;
830                 if (p) {
831                         *p = 0;
832                         next = p+1;
833                 }
834                 arg = strchr(token, '=');
835                 if (arg) {
836                         *arg = 0;
837                         arg++;
838                 }
839                 if (!strcmp(token, "test_fs")) {
840                         fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
841                         printf("Setting test filesystem flag\n");
842                         ext2fs_mark_super_dirty(fs);
843                 } else if (!strcmp(token, "^test_fs")) {
844                         fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
845                         printf("Clearing test filesystem flag\n");
846                         ext2fs_mark_super_dirty(fs);
847                 } else if (strcmp(token, "stride") == 0) {
848                         if (!arg) {
849                                 r_usage++;
850                                 continue;
851                         }
852                         stride = strtoul(arg, &p, 0);
853                         if (*p || (stride == 0)) {
854                                 fprintf(stderr,
855                                        _("Invalid RAID stride: %s\n"),
856                                         arg);
857                                 r_usage++;
858                                 continue;
859                         }
860                         stride_set = 1;
861                 } else if (strcmp(token, "stripe-width") == 0 ||
862                            strcmp(token, "stripe_width") == 0) {
863                         if (!arg) {
864                                 r_usage++;
865                                 continue;
866                         }
867                         stripe_width = strtoul(arg, &p, 0);
868                         if (*p || (stripe_width == 0)) {
869                                 fprintf(stderr,
870                                         _("Invalid RAID stripe-width: %s\n"),
871                                         arg);
872                                 r_usage++;
873                                 continue;
874                         }
875                         stripe_width_set = 1;
876                 } else 
877                         r_usage++;
878         }
879         if (r_usage) {
880                 fprintf(stderr, _("\nBad options specified.\n\n"
881                         "Extended options are separated by commas, "
882                         "and may take an argument which\n"
883                         "\tis set off by an equals ('=') sign.\n\n"
884                         "Valid extended options are:\n"
885                         "\tstride=<RAID per-disk chunk size in blocks>\n"
886                         "\tstripe-width=<RAID stride*data disks in blocks>\n"
887                         "\ttest_fs\n"
888                         "\t^test_fs\n"));
889                 free(buf);
890                 exit(1);
891         }
892         free(buf);
893 }       
894
895
896 int main (int argc, char ** argv)
897 {
898         errcode_t retval;
899         ext2_filsys fs;
900         struct ext2_super_block *sb;
901         io_manager io_ptr;
902
903 #ifdef ENABLE_NLS
904         setlocale(LC_MESSAGES, "");
905         setlocale(LC_CTYPE, "");
906         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
907         textdomain(NLS_CAT_NAME);
908 #endif
909         if (argc && *argv)
910                 program_name = *argv;
911         add_error_table(&et_ext2_error_table);
912
913         if (strcmp(get_progname(argv[0]), "findfs") == 0)
914                 do_findfs(argc, argv);
915         if (strcmp(get_progname(argv[0]), "e2label") == 0)
916                 parse_e2label_options(argc, argv);
917         else
918                 parse_tune2fs_options(argc, argv);
919         
920 #ifdef CONFIG_TESTIO_DEBUG
921         io_ptr = test_io_manager;
922         test_io_backing_manager = unix_io_manager;
923 #else
924         io_ptr = unix_io_manager;
925 #endif
926         retval = ext2fs_open2(device_name, io_options, open_flag, 
927                               0, 0, io_ptr, &fs);
928         if (retval) {
929                 com_err (program_name, retval, _("while trying to open %s"),
930                          device_name);
931                 fprintf(stderr,
932                         _("Couldn't find valid filesystem superblock.\n"));
933                 exit(1);
934         }
935         sb = fs->super;
936         fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
937         if ((sb->s_feature_incompat & !EXT2_TUNE2FS_INCOMPAT) ||
938             (sb->s_feature_ro_compat & !EXT2_TUNE2FS_RO_COMPAT)) {
939                 fprintf(stderr, 
940                         _("Filesystem %s has unsupported features enabled.\n"),
941                         device_name);
942                 exit(1);
943         }
944         if (print_label) {
945                 /* For e2label emulation */
946                 printf("%.*s\n", (int) sizeof(sb->s_volume_name),
947                        sb->s_volume_name);
948                 remove_error_table(&et_ext2_error_table);
949                 exit(0);
950         }
951         retval = ext2fs_check_if_mounted(device_name, &mount_flags);
952         if (retval) {
953                 com_err("ext2fs_check_if_mount", retval,
954                         _("while determining whether %s is mounted."),
955                         device_name);
956                 exit(1);
957         }
958         /* Normally we only need to write out the superblock */
959         fs->flags |= EXT2_FLAG_SUPER_ONLY;
960
961         if (c_flag) {
962                 sb->s_max_mnt_count = max_mount_count;
963                 ext2fs_mark_super_dirty(fs);
964                 printf (_("Setting maximal mount count to %d\n"),
965                         max_mount_count);
966         }
967         if (C_flag) {
968                 sb->s_mnt_count = mount_count;
969                 ext2fs_mark_super_dirty(fs);
970                 printf (_("Setting current mount count to %d\n"), mount_count);
971         }
972         if (e_flag) {
973                 sb->s_errors = errors;
974                 ext2fs_mark_super_dirty(fs);
975                 printf (_("Setting error behavior to %d\n"), errors);
976         }
977         if (g_flag) {
978                 sb->s_def_resgid = resgid;
979                 ext2fs_mark_super_dirty(fs);
980                 printf (_("Setting reserved blocks gid to %lu\n"), resgid);
981         }
982         if (i_flag) {
983                 sb->s_checkinterval = interval;
984                 ext2fs_mark_super_dirty(fs);
985                 printf (_("Setting interval between checks to %lu seconds\n"), interval);
986         }
987         if (m_flag) {
988                 sb->s_r_blocks_count = e2p_percent(reserved_ratio,
989                                                    sb->s_blocks_count);
990                 ext2fs_mark_super_dirty(fs);
991                 printf (_("Setting reserved blocks percentage to %g%% (%u blocks)\n"),
992                         reserved_ratio, sb->s_r_blocks_count);
993         }
994         if (r_flag) {
995                 if (reserved_blocks >= sb->s_blocks_count/2) {
996                         com_err (program_name, 0,
997                                  _("reserved blocks count is too big (%lu)"),
998                                  reserved_blocks);
999                         exit (1);
1000                 }
1001                 sb->s_r_blocks_count = reserved_blocks;
1002                 ext2fs_mark_super_dirty(fs);
1003                 printf (_("Setting reserved blocks count to %lu\n"),
1004                         reserved_blocks);
1005         }
1006         if (s_flag == 1) {
1007                 if (sb->s_feature_ro_compat &
1008                     EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
1009                         fputs(_("\nThe filesystem already has sparse "
1010                                 "superblocks.\n"), stderr);
1011                 else {
1012                         sb->s_feature_ro_compat |=
1013                                 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
1014                         sb->s_state &= ~EXT2_VALID_FS;
1015                         ext2fs_mark_super_dirty(fs);
1016                         printf(_("\nSparse superblock flag set.  %s"),
1017                                _(please_fsck));
1018                 }
1019         }
1020         if (s_flag == 0) {
1021                 fputs(_("\nClearing the sparse superflag not supported.\n"),
1022                       stderr);
1023                 exit(1);
1024         }
1025         if (T_flag) {
1026                 sb->s_lastcheck = last_check_time;
1027                 ext2fs_mark_super_dirty(fs);
1028                 printf(_("Setting time filesystem last checked to %s\n"),
1029                        ctime(&last_check_time));
1030         }
1031         if (u_flag) {
1032                 sb->s_def_resuid = resuid;
1033                 ext2fs_mark_super_dirty(fs);
1034                 printf (_("Setting reserved blocks uid to %lu\n"), resuid);
1035         }
1036         if (L_flag) {
1037                 if (strlen(new_label) > sizeof(sb->s_volume_name))
1038                         fputs(_("Warning: label too long, truncating.\n"), 
1039                               stderr);
1040                 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
1041                 strncpy(sb->s_volume_name, new_label,
1042                         sizeof(sb->s_volume_name));
1043                 ext2fs_mark_super_dirty(fs);
1044         }
1045         if (M_flag) {
1046                 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
1047                 strncpy(sb->s_last_mounted, new_last_mounted,
1048                         sizeof(sb->s_last_mounted));
1049                 ext2fs_mark_super_dirty(fs);
1050         }
1051         if (mntopts_cmd)
1052                 update_mntopts(fs, mntopts_cmd);
1053         if (features_cmd)
1054                 update_feature_set(fs, features_cmd);
1055         if (extended_cmd)
1056                 parse_extended_opts(fs, extended_cmd);
1057         if (journal_size || journal_device)
1058                 add_journal(fs);
1059         
1060         if (U_flag) {
1061                 if ((strcasecmp(new_UUID, "null") == 0) ||
1062                     (strcasecmp(new_UUID, "clear") == 0)) {
1063                         uuid_clear(sb->s_uuid);
1064                 } else if (strcasecmp(new_UUID, "time") == 0) {
1065                         uuid_generate_time(sb->s_uuid);
1066                 } else if (strcasecmp(new_UUID, "random") == 0) {
1067                         uuid_generate(sb->s_uuid);
1068                 } else if (uuid_parse(new_UUID, sb->s_uuid)) {
1069                         com_err(program_name, 0, _("Invalid UUID format\n"));
1070                         exit(1);
1071                 }
1072                 ext2fs_mark_super_dirty(fs);
1073         }
1074
1075         if (l_flag)
1076                 list_super (sb);
1077         if (stride_set) {
1078                 sb->s_raid_stride = stride;
1079                 ext2fs_mark_super_dirty(fs);
1080                 printf(_("Setting stride size to %d\n"), stride);
1081         }
1082         if (stripe_width_set) {
1083                 sb->s_raid_stripe_width = stripe_width;
1084                 ext2fs_mark_super_dirty(fs);
1085                 printf(_("Setting stripe width to %d\n"), stripe_width);
1086         }
1087         remove_error_table(&et_ext2_error_table);
1088         return (ext2fs_close (fs) ? 1 : 0);
1089 }