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