2 * tune2fs.c - Change the file system parameters on an ext2 file system
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)
8 * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
11 * This file may be redistributed under the terms of the GNU Public
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)
42 #include <sys/types.h>
44 #include "ext2fs/ext2_fs.h"
45 #include "ext2fs/ext2fs.h"
46 #include "et/com_err.h"
47 #include "uuid/uuid.h"
51 #include "blkid/blkid.h"
53 #include "../version.h"
54 #include "nls-enable.h"
56 const char * program_name = "tune2fs";
58 char * new_label, *new_last_mounted, *new_UUID;
59 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
60 static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
61 static time_t last_check_time;
62 static int print_label;
63 static int max_mount_count, mount_count, mount_flags;
64 static unsigned long interval, reserved_ratio, reserved_blocks;
65 static unsigned long resgid, resuid;
66 static unsigned short errors;
68 static char *features_cmd;
69 static char *mntopts_cmd;
71 int journal_size, journal_flags;
74 static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
76 void do_findfs(int argc, char **argv);
78 static void usage(void)
81 _("Usage: %s [-c max-mounts-count] [-e errors-behavior] "
83 "\t[-i interval[d|m|w]] [-j] [-J journal-options]\n"
84 "\t[-l] [-s sparse-flag] [-m reserved-blocks-percent]\n"
85 "\t[-o [^]mount-options[,...]] [-r reserved-blocks-count]\n"
86 "\t[-u user] [-C mount-count] [-L volume-label] "
87 "[-M last-mounted-dir]\n"
88 "\t[-O [^]feature[,...]] [-T last-check-time] [-U UUID]"
89 " device\n"), program_name);
93 static __u32 ok_features[3] = {
94 EXT3_FEATURE_COMPAT_HAS_JOURNAL |
95 EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */
96 EXT2_FEATURE_INCOMPAT_FILETYPE, /* Incompat */
97 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */
101 * Remove an external journal from the filesystem
103 static void remove_journal_device(ext2_filsys fs)
108 journal_superblock_t *jsb;
111 int commit_remove_journal = 0;
115 commit_remove_journal = 1; /* force removal even if error */
117 uuid_unparse(fs->super->s_journal_uuid, buf);
118 journal_path = blkid_get_devname(NULL, "UUID", buf);
122 ext2fs_find_block_device(fs->super->s_journal_dev);
127 #ifdef CONFIG_TESTIO_DEBUG
128 io_ptr = test_io_manager;
129 test_io_backing_manager = unix_io_manager;
131 io_ptr = unix_io_manager;
133 retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
134 EXT2_FLAG_JOURNAL_DEV_OK, 0,
135 fs->blocksize, io_ptr, &jfs);
137 com_err(program_name, retval,
138 _("while trying to open external journal"));
139 goto no_valid_journal;
141 if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
142 fprintf(stderr, _("%s is not a journal device.\n"),
144 goto no_valid_journal;
147 /* Get the journal superblock */
148 if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
149 com_err(program_name, retval,
150 _("while reading journal superblock"));
151 goto no_valid_journal;
154 jsb = (journal_superblock_t *) buf;
155 if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
156 (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
157 fprintf(stderr, _("Journal superblock not found!\n"));
158 goto no_valid_journal;
161 /* Find the filesystem UUID */
162 nr_users = ntohl(jsb->s_nr_users);
163 for (i=0; i < nr_users; i++) {
164 if (memcmp(fs->super->s_uuid,
165 &jsb->s_users[i*16], 16) == 0)
170 _("Filesystem's UUID not found on journal device.\n"));
171 commit_remove_journal = 1;
172 goto no_valid_journal;
175 for (i=0; i < nr_users; i++)
176 memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
177 jsb->s_nr_users = htonl(nr_users);
179 /* Write back the journal superblock */
180 if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
181 com_err(program_name, retval,
182 "while writing journal superblock.");
183 goto no_valid_journal;
186 commit_remove_journal = 1;
189 if (commit_remove_journal == 0) {
190 fprintf(stderr, _("Journal NOT removed\n"));
193 fs->super->s_journal_dev = 0;
194 uuid_clear(fs->super->s_journal_uuid);
195 ext2fs_mark_super_dirty(fs);
196 printf(_("Journal removed\n"));
200 /* Helper function for remove_journal_inode */
201 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
202 int blockcnt, void *private)
208 ext2fs_unmark_block_bitmap(fs->block_map,block);
209 group = ext2fs_group_of_blk(fs, block);
210 fs->group_desc[group].bg_free_blocks_count++;
211 fs->super->s_free_blocks_count++;
216 * Remove the journal inode from the filesystem
218 static void remove_journal_inode(ext2_filsys fs)
220 struct ext2_inode inode;
222 ino_t ino = fs->super->s_journal_inum;
224 retval = ext2fs_read_inode(fs, ino, &inode);
226 com_err(program_name, retval,
227 _("while reading journal inode"));
230 if (ino == EXT2_JOURNAL_INO) {
231 retval = ext2fs_read_bitmaps(fs);
233 com_err(program_name, retval,
234 _("while reading bitmaps"));
237 retval = ext2fs_block_iterate(fs, ino, 0, NULL,
238 release_blocks_proc, NULL);
240 com_err(program_name, retval,
241 _("while clearing journal inode"));
244 memset(&inode, 0, sizeof(inode));
245 ext2fs_mark_bb_dirty(fs);
246 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
248 inode.i_flags &= ~EXT2_IMMUTABLE_FL;
249 retval = ext2fs_write_inode(fs, ino, &inode);
251 com_err(program_name, retval,
252 _("while writing journal inode"));
255 fs->super->s_journal_inum = 0;
256 ext2fs_mark_super_dirty(fs);
260 * Update the default mount options
262 static void update_mntopts(ext2_filsys fs, char *mntopts)
264 struct ext2_super_block *sb= fs->super;
266 if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
267 fprintf(stderr, _("Invalid mount option set: %s\n"),
271 ext2fs_mark_super_dirty(fs);
275 * Update the feature set as provided by the user.
277 static void update_feature_set(ext2_filsys fs, char *features)
279 int sparse, old_sparse, filetype, old_filetype;
280 int journal, old_journal, dxdir, old_dxdir;
281 struct ext2_super_block *sb= fs->super;
283 old_sparse = sb->s_feature_ro_compat &
284 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
285 old_filetype = sb->s_feature_incompat &
286 EXT2_FEATURE_INCOMPAT_FILETYPE;
287 old_journal = sb->s_feature_compat &
288 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
289 old_dxdir = sb->s_feature_compat &
290 EXT2_FEATURE_COMPAT_DIR_INDEX;
291 if (e2p_edit_feature(features, &sb->s_feature_compat,
293 fprintf(stderr, _("Invalid filesystem option set: %s\n"),
297 sparse = sb->s_feature_ro_compat &
298 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
299 filetype = sb->s_feature_incompat &
300 EXT2_FEATURE_INCOMPAT_FILETYPE;
301 journal = sb->s_feature_compat &
302 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
303 dxdir = sb->s_feature_compat &
304 EXT2_FEATURE_COMPAT_DIR_INDEX;
305 if (old_journal && !journal) {
306 if ((mount_flags & EXT2_MF_MOUNTED) &&
307 !(mount_flags & EXT2_MF_READONLY)) {
309 _("The has_journal flag may only be "
310 "cleared when the filesystem is\n"
311 "unmounted or mounted "
315 if (sb->s_feature_incompat &
316 EXT3_FEATURE_INCOMPAT_RECOVER) {
318 _("The needs_recovery flag is set. "
319 "Please run e2fsck before clearing\n"
320 "the has_journal flag.\n"));
323 if (sb->s_journal_inum) {
324 remove_journal_inode(fs);
326 if (sb->s_journal_dev) {
327 remove_journal_device(fs);
330 if (journal && !old_journal) {
332 * If adding a journal flag, let the create journal
333 * code below handle creating setting the flag and
334 * creating the journal. We supply a default size if
339 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
341 if (dxdir && !old_dxdir) {
342 if (!sb->s_def_hash_version)
343 sb->s_def_hash_version = EXT2_HASH_TEA;
344 if (uuid_is_null((unsigned char *) sb->s_hash_seed))
345 uuid_generate((unsigned char *) sb->s_hash_seed);
348 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
349 (sb->s_feature_compat || sb->s_feature_ro_compat ||
350 sb->s_feature_incompat))
351 ext2fs_update_dynamic_rev(fs);
352 if ((sparse != old_sparse) ||
353 (filetype != old_filetype)) {
354 sb->s_state &= ~EXT2_VALID_FS;
355 printf("\n%s\n", _(please_fsck));
357 ext2fs_mark_super_dirty(fs);
361 * Add a journal to the filesystem.
363 static void add_journal(ext2_filsys fs)
365 unsigned long journal_blocks;
370 if (fs->super->s_feature_compat &
371 EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
372 fprintf(stderr, _("The filesystem already has a journal.\n"));
375 if (journal_device) {
376 check_plausibility(journal_device);
377 check_mount(journal_device, 0, _("journal"));
378 #ifdef CONFIG_TESTIO_DEBUG
379 io_ptr = test_io_manager;
380 test_io_backing_manager = unix_io_manager;
382 io_ptr = unix_io_manager;
384 retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
385 EXT2_FLAG_JOURNAL_DEV_OK, 0,
386 fs->blocksize, io_ptr, &jfs);
388 com_err(program_name, retval,
389 _("\n\twhile trying to open journal on %s\n"),
393 printf(_("Creating journal on device %s: "),
397 retval = ext2fs_add_journal_device(fs, jfs);
400 com_err (program_name, retval,
401 _("while adding filesystem to journal on %s"),
406 } else if (journal_size) {
407 printf(_("Creating journal inode: "));
409 journal_blocks = figure_journal_size(journal_size, fs);
411 retval = ext2fs_add_journal_inode(fs, journal_blocks,
414 fprintf(stderr, "\n");
415 com_err(program_name, retval,
416 _("\n\twhile trying to create journal file"));
421 * If the filesystem wasn't mounted, we need to force
422 * the block group descriptors out.
424 if ((mount_flags & EXT2_MF_MOUNTED) == 0)
425 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
427 print_check_message(fs);
432 free(journal_device);
437 static void parse_e2label_options(int argc, char ** argv)
439 if ((argc < 2) || (argc > 3)) {
440 fprintf(stderr, _("Usage: e2label device [newlabel]\n"));
443 device_name = blkid_get_devname(NULL, argv[1], NULL);
445 com_err("e2label", 0, _("Unable to resolve '%s'"),
450 open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK;
457 static time_t parse_time(char *str)
461 if (strcmp(str, "now") == 0) {
464 memset(&ts, 0, sizeof(ts));
466 strptime(optarg, "%Y%m%d%H%M%S", &ts);
468 sscanf(optarg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
469 &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
472 if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
473 ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
474 ts.tm_min > 59 || ts.tm_sec > 61)
477 if (ts.tm_mday == 0) {
478 com_err(program_name, 0,
479 _("Couldn't parse date/time specifier: %s"),
483 return (mktime(&ts));
486 static void parse_tune2fs_options(int argc, char **argv)
493 printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
494 while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF)
498 max_mount_count = strtol (optarg, &tmp, 0);
499 if (*tmp || max_mount_count > 16000) {
500 com_err (program_name, 0,
501 _("bad mounts count - %s"),
505 if (max_mount_count == 0)
506 max_mount_count = -1;
508 open_flag = EXT2_FLAG_RW;
511 mount_count = strtoul (optarg, &tmp, 0);
512 if (*tmp || mount_count > 16000) {
513 com_err (program_name, 0,
514 _("bad mounts count - %s"),
519 open_flag = EXT2_FLAG_RW;
522 if (strcmp (optarg, "continue") == 0)
523 errors = EXT2_ERRORS_CONTINUE;
524 else if (strcmp (optarg, "remount-ro") == 0)
525 errors = EXT2_ERRORS_RO;
526 else if (strcmp (optarg, "panic") == 0)
527 errors = EXT2_ERRORS_PANIC;
529 com_err (program_name, 0,
530 _("bad error behavior - %s"),
535 open_flag = EXT2_FLAG_RW;
537 case 'f': /* Force */
541 resgid = strtoul (optarg, &tmp, 0);
543 gr = getgrnam (optarg);
552 com_err (program_name, 0,
553 _("bad gid/group name - %s"),
558 open_flag = EXT2_FLAG_RW;
561 interval = strtoul (optarg, &tmp, 0);
574 case 'M': /* months! */
575 interval *= 86400 * 30;
579 case 'W': /* weeks */
580 interval *= 86400 * 7;
584 if (*tmp || interval > (365 * 86400)) {
585 com_err (program_name, 0,
586 _("bad interval - %s"), optarg);
590 open_flag = EXT2_FLAG_RW;
595 open_flag = EXT2_FLAG_RW;
598 parse_journal_opts(optarg);
599 open_flag = EXT2_FLAG_RW;
607 open_flag = EXT2_FLAG_RW |
608 EXT2_FLAG_JOURNAL_DEV_OK;
611 reserved_ratio = strtoul (optarg, &tmp, 0);
612 if (*tmp || reserved_ratio > 50) {
613 com_err (program_name, 0,
614 _("bad reserved block ratio - %s"),
619 open_flag = EXT2_FLAG_RW;
622 new_last_mounted = optarg;
624 open_flag = EXT2_FLAG_RW;
628 com_err (program_name, 0,
629 _("-o may only be specified once"));
632 mntopts_cmd = optarg;
633 open_flag = EXT2_FLAG_RW;
638 com_err (program_name, 0,
639 _("-O may only be specified once"));
642 features_cmd = optarg;
643 open_flag = EXT2_FLAG_RW;
646 reserved_blocks = strtoul (optarg, &tmp, 0);
648 com_err (program_name, 0,
649 _("bad reserved blocks count - %s"),
654 open_flag = EXT2_FLAG_RW;
657 s_flag = atoi(optarg);
658 open_flag = EXT2_FLAG_RW;
662 last_check_time = parse_time(optarg);
663 open_flag = EXT2_FLAG_RW;
666 resuid = strtoul (optarg, &tmp, 0);
668 pw = getpwnam (optarg);
677 com_err (program_name, 0,
678 _("bad uid/user name - %s"),
683 open_flag = EXT2_FLAG_RW;
688 open_flag = EXT2_FLAG_RW |
689 EXT2_FLAG_JOURNAL_DEV_OK;
694 if (optind < argc - 1 || optind == argc)
696 if (!open_flag && !l_flag)
698 device_name = blkid_get_devname(NULL, argv[optind], NULL);
700 com_err("tune2fs", 0, _("Unable to resolve '%s'"),
706 void do_findfs(int argc, char **argv)
711 (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
712 fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
715 dev = blkid_get_devname(NULL, argv[1], NULL);
717 com_err("findfs", 0, _("Unable to resolve '%s'"),
726 int main (int argc, char ** argv)
730 struct ext2_super_block *sb;
734 setlocale(LC_MESSAGES, "");
735 setlocale(LC_CTYPE, "");
736 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
737 textdomain(NLS_CAT_NAME);
740 program_name = *argv;
741 initialize_ext2_error_table();
743 if (strcmp(get_progname(argv[0]), "findfs") == 0)
744 do_findfs(argc, argv);
745 if (strcmp(get_progname(argv[0]), "e2label") == 0)
746 parse_e2label_options(argc, argv);
748 parse_tune2fs_options(argc, argv);
750 #ifdef CONFIG_TESTIO_DEBUG
751 io_ptr = test_io_manager;
752 test_io_backing_manager = unix_io_manager;
754 io_ptr = unix_io_manager;
756 retval = ext2fs_open (device_name, open_flag, 0, 0, io_ptr, &fs);
758 com_err (program_name, retval, _("while trying to open %s"),
761 _("Couldn't find valid filesystem superblock.\n"));
766 /* For e2label emulation */
767 printf("%.*s\n", (int) sizeof(sb->s_volume_name),
771 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
773 com_err("ext2fs_check_if_mount", retval,
774 _("while determining whether %s is mounted."),
778 /* Normally we only need to write out the superblock */
779 fs->flags |= EXT2_FLAG_SUPER_ONLY;
782 sb->s_max_mnt_count = max_mount_count;
783 ext2fs_mark_super_dirty(fs);
784 printf (_("Setting maximal mount count to %d\n"),
788 sb->s_mnt_count = mount_count;
789 ext2fs_mark_super_dirty(fs);
790 printf (_("Setting current mount count to %d\n"), mount_count);
793 sb->s_errors = errors;
794 ext2fs_mark_super_dirty(fs);
795 printf (_("Setting error behavior to %d\n"), errors);
798 sb->s_def_resgid = resgid;
799 ext2fs_mark_super_dirty(fs);
800 printf (_("Setting reserved blocks gid to %lu\n"), resgid);
803 sb->s_checkinterval = interval;
804 ext2fs_mark_super_dirty(fs);
805 printf (_("Setting interval between check %lu seconds\n"), interval);
808 sb->s_r_blocks_count = (sb->s_blocks_count / 100)
810 ext2fs_mark_super_dirty(fs);
811 printf (_("Setting reserved blocks percentage to %lu (%u blocks)\n"),
812 reserved_ratio, sb->s_r_blocks_count);
815 if (reserved_blocks >= sb->s_blocks_count) {
816 com_err (program_name, 0,
817 _("reserved blocks count is too big (%ul)"),
821 sb->s_r_blocks_count = reserved_blocks;
822 ext2fs_mark_super_dirty(fs);
823 printf (_("Setting reserved blocks count to %lu\n"),
827 if (sb->s_feature_ro_compat &
828 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
829 fprintf(stderr, _("\nThe filesystem already"
830 " has sparse superblocks.\n"));
832 sb->s_feature_ro_compat |=
833 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
834 sb->s_state &= ~EXT2_VALID_FS;
835 ext2fs_mark_super_dirty(fs);
836 printf(_("\nSparse superblock flag set. %s"),
841 if (!(sb->s_feature_ro_compat &
842 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
843 fprintf(stderr, _("\nThe filesystem already"
844 " has sparse superblocks disabled.\n"));
846 sb->s_feature_ro_compat &=
847 ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
848 sb->s_state &= ~EXT2_VALID_FS;
849 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
850 ext2fs_mark_super_dirty(fs);
851 printf(_("\nSparse superblock flag cleared. %s"),
856 sb->s_lastcheck = last_check_time;
857 ext2fs_mark_super_dirty(fs);
858 printf(_("Setting time filesystem last checked to %s\n"),
859 ctime(&last_check_time));
862 sb->s_def_resuid = resuid;
863 ext2fs_mark_super_dirty(fs);
864 printf (_("Setting reserved blocks uid to %lu\n"), resuid);
867 if (strlen(new_label) > sizeof(sb->s_volume_name))
868 fprintf(stderr, _("Warning: label too "
869 "long, truncating.\n"));
870 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
871 strncpy(sb->s_volume_name, new_label,
872 sizeof(sb->s_volume_name));
873 ext2fs_mark_super_dirty(fs);
876 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
877 strncpy(sb->s_last_mounted, new_last_mounted,
878 sizeof(sb->s_last_mounted));
879 ext2fs_mark_super_dirty(fs);
882 update_mntopts(fs, mntopts_cmd);
884 update_feature_set(fs, features_cmd);
885 if (journal_size || journal_device)
889 if ((strcasecmp(new_UUID, "null") == 0) ||
890 (strcasecmp(new_UUID, "clear") == 0)) {
891 uuid_clear(sb->s_uuid);
892 } else if (strcasecmp(new_UUID, "time") == 0) {
893 uuid_generate_time(sb->s_uuid);
894 } else if (strcasecmp(new_UUID, "random") == 0) {
895 uuid_generate(sb->s_uuid);
896 } else if (uuid_parse(new_UUID, sb->s_uuid)) {
897 com_err(program_name, 0, _("Invalid UUID format\n"));
900 ext2fs_mark_super_dirty(fs);
905 return (ext2fs_close (fs) ? 1 : 0);