1 /* vi: set sw=4 ts=4: */
3 * tune2fs.c - Change the file system parameters on an ext2 file system
5 * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
6 * Laboratoire MASI, Institut Blaise Pascal
7 * Universite Pierre et Marie Curie (Paris VI)
9 * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
11 * Licensed under GPLv2, see file LICENSE in this source tree.
14 ////usage:#define tune2fs_trivial_usage
15 ////usage: "[-c MOUNT_CNT] "
16 ////usage: "[-e errors-behavior] [-g group] "
17 ////usage: "[-i DAYS] "
18 ////usage: "[-j] [-J journal-options] [-l] [-s sparse-flag] "
19 ////usage: "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] "
20 ////usage: "[-r reserved-blocks-count] [-u user] [-C mount-count] "
21 ////usage: "[-L LABEL] "
22 ////usage: "[-M last-mounted-dir] [-O [^]feature[,...]] "
23 ////usage: "[-T last-check-time] [-U UUID] "
26 ////usage:#define tune2fs_full_usage "\n\n"
27 ////usage: "Adjust filesystem options on ext[23] filesystems"
28 //applet:IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, _BB_DIR_SBIN, _BB_SUID_DROP, tune2fs))
33 * 93/10/31 - Added the -c option to change the maximal mount counts
34 * 93/12/14 - Added -l flag to list contents of superblock
35 * M.J.E. Mol (marcel@duteca.et.tudelft.nl)
36 * F.W. ten Wolde (franky@duteca.et.tudelft.nl)
37 * 93/12/29 - Added the -e option to change errors behavior
38 * 94/02/27 - Ported to use the ext2fs library
39 * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
42 #include <sys/types.h>
52 #include "ext2fs/ext2_fs.h"
53 #include "ext2fs/ext2fs.h"
54 #include "../e2fs_lib.h"
56 #include "ext2fs/kernel-jbd.h"
58 #include "volume_id.h"
62 static char * device_name
= NULL
;
63 static char * new_label
, *new_last_mounted
, *new_UUID
;
64 static char * io_options
;
65 static int c_flag
, C_flag
, e_flag
, f_flag
, g_flag
, i_flag
, l_flag
, L_flag
;
66 static int m_flag
, M_flag
, r_flag
, s_flag
= -1, u_flag
, U_flag
, T_flag
;
67 static time_t last_check_time
;
68 static int print_label
;
69 static int max_mount_count
, mount_count
, mount_flags
;
70 static unsigned long interval
, reserved_blocks
;
71 static unsigned reserved_ratio
;
72 static unsigned long resgid
, resuid
;
73 static unsigned short errors
;
75 static char *features_cmd
;
76 static char *mntopts_cmd
;
78 static int journal_size
, journal_flags
;
79 static char *journal_device
= NULL
;
81 static const char *please_fsck
= "Please run e2fsck on the filesystem\n";
83 static __u32 ok_features
[3] = {
84 EXT3_FEATURE_COMPAT_HAS_JOURNAL
| EXT2_FEATURE_COMPAT_DIR_INDEX
,
85 EXT2_FEATURE_INCOMPAT_FILETYPE
,
86 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
90 * Remove an external journal from the filesystem
92 static void remove_journal_device(ext2_filsys fs
)
97 journal_superblock_t
*jsb
;
100 int commit_remove_journal
= 0;
104 commit_remove_journal
= 1; /* force removal even if error */
106 unparse_uuid(fs
->super
->s_journal_uuid
, buf
);
107 journal_path
= get_devname_from_uuid(buf
);
111 ext2fs_find_block_device(fs
->super
->s_journal_dev
);
116 io_ptr
= unix_io_manager
;
117 retval
= ext2fs_open(journal_path
, EXT2_FLAG_RW
|
118 EXT2_FLAG_JOURNAL_DEV_OK
, 0,
119 fs
->blocksize
, io_ptr
, &jfs
);
121 bb_error_msg("Failed to open external journal");
122 goto no_valid_journal
;
124 if (!(jfs
->super
->s_feature_incompat
& EXT3_FEATURE_INCOMPAT_JOURNAL_DEV
)) {
125 bb_error_msg("%s is not a journal device", journal_path
);
126 goto no_valid_journal
;
129 /* Get the journal superblock */
130 if ((retval
= io_channel_read_blk(jfs
->io
, 1, -1024, buf
))) {
131 bb_error_msg("Failed to read journal superblock");
132 goto no_valid_journal
;
135 jsb
= (journal_superblock_t
*) buf
;
136 if ((jsb
->s_header
.h_magic
!= (unsigned) ntohl(JFS_MAGIC_NUMBER
)) ||
137 (jsb
->s_header
.h_blocktype
!= (unsigned) ntohl(JFS_SUPERBLOCK_V2
))) {
138 bb_error_msg("Journal superblock not found!");
139 goto no_valid_journal
;
142 /* Find the filesystem UUID */
143 nr_users
= ntohl(jsb
->s_nr_users
);
144 for (i
=0; i
< nr_users
; i
++) {
145 if (memcmp(fs
->super
->s_uuid
,
146 &jsb
->s_users
[i
*16], 16) == 0)
150 bb_error_msg("Filesystem's UUID not found on journal device");
151 commit_remove_journal
= 1;
152 goto no_valid_journal
;
155 for (i
=0; i
< nr_users
; i
++)
156 memcpy(&jsb
->s_users
[i
*16], &jsb
->s_users
[(i
+1)*16], 16);
157 jsb
->s_nr_users
= htonl(nr_users
);
159 /* Write back the journal superblock */
160 if ((retval
= io_channel_write_blk(jfs
->io
, 1, -1024, buf
))) {
161 bb_error_msg("Failed to write journal superblock");
162 goto no_valid_journal
;
165 commit_remove_journal
= 1;
168 if (commit_remove_journal
== 0)
169 bb_error_msg_and_die("Journal NOT removed");
170 fs
->super
->s_journal_dev
= 0;
171 uuid_clear(fs
->super
->s_journal_uuid
);
172 ext2fs_mark_super_dirty(fs
);
173 puts("Journal removed");
177 /* Helper function for remove_journal_inode */
178 static int release_blocks_proc(ext2_filsys fs
, blk_t
*blocknr
,
179 int blockcnt
EXT2FS_ATTR((unused
)),
180 void *private EXT2FS_ATTR((unused
)))
186 ext2fs_unmark_block_bitmap(fs
->block_map
,block
);
187 group
= ext2fs_group_of_blk(fs
, block
);
188 fs
->group_desc
[group
].bg_free_blocks_count
++;
189 fs
->super
->s_free_blocks_count
++;
194 * Remove the journal inode from the filesystem
196 static void remove_journal_inode(ext2_filsys fs
)
198 struct ext2_inode inode
;
200 ino_t ino
= fs
->super
->s_journal_inum
;
201 const char *msg
= "to read";
202 const char *s
= "journal inode";
204 retval
= ext2fs_read_inode(fs
, ino
, &inode
);
206 goto REMOVE_JOURNAL_INODE_ERROR
;
207 if (ino
== EXT2_JOURNAL_INO
) {
208 retval
= ext2fs_read_bitmaps(fs
);
210 msg
= "to read bitmaps";
212 goto REMOVE_JOURNAL_INODE_ERROR
;
214 retval
= ext2fs_block_iterate(fs
, ino
, 0, NULL
,
215 release_blocks_proc
, NULL
);
218 goto REMOVE_JOURNAL_INODE_ERROR
;
220 memset(&inode
, 0, sizeof(inode
));
221 ext2fs_mark_bb_dirty(fs
);
222 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
224 inode
.i_flags
&= ~EXT2_IMMUTABLE_FL
;
225 retval
= ext2fs_write_inode(fs
, ino
, &inode
);
228 REMOVE_JOURNAL_INODE_ERROR
:
229 bb_error_msg_and_die("Failed %s %s", msg
, s
);
231 fs
->super
->s_journal_inum
= 0;
232 ext2fs_mark_super_dirty(fs
);
236 * Update the default mount options
238 static void update_mntopts(ext2_filsys fs
, char *mntopts
)
240 struct ext2_super_block
*sb
= fs
->super
;
242 if (e2p_edit_mntopts(mntopts
, &sb
->s_default_mount_opts
, ~0))
243 bb_error_msg_and_die("Invalid mount option set: %s", mntopts
);
244 ext2fs_mark_super_dirty(fs
);
248 * Update the feature set as provided by the user.
250 static void update_feature_set(ext2_filsys fs
, char *features
)
252 int sparse
, old_sparse
, filetype
, old_filetype
;
253 int journal
, old_journal
, dxdir
, old_dxdir
;
254 struct ext2_super_block
*sb
= fs
->super
;
255 __u32 old_compat
, old_incompat
, old_ro_compat
;
257 old_compat
= sb
->s_feature_compat
;
258 old_ro_compat
= sb
->s_feature_ro_compat
;
259 old_incompat
= sb
->s_feature_incompat
;
261 old_sparse
= sb
->s_feature_ro_compat
&
262 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
263 old_filetype
= sb
->s_feature_incompat
&
264 EXT2_FEATURE_INCOMPAT_FILETYPE
;
265 old_journal
= sb
->s_feature_compat
&
266 EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
267 old_dxdir
= sb
->s_feature_compat
&
268 EXT2_FEATURE_COMPAT_DIR_INDEX
;
269 if (e2p_edit_feature(features
, &sb
->s_feature_compat
, ok_features
))
270 bb_error_msg_and_die("Invalid filesystem option set: %s", features
);
271 sparse
= sb
->s_feature_ro_compat
&
272 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
273 filetype
= sb
->s_feature_incompat
&
274 EXT2_FEATURE_INCOMPAT_FILETYPE
;
275 journal
= sb
->s_feature_compat
&
276 EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
277 dxdir
= sb
->s_feature_compat
&
278 EXT2_FEATURE_COMPAT_DIR_INDEX
;
279 if (old_journal
&& !journal
) {
280 if ((mount_flags
& EXT2_MF_MOUNTED
) &&
281 !(mount_flags
& EXT2_MF_READONLY
)) {
282 bb_error_msg_and_die(
283 "The has_journal flag may only be "
284 "cleared when the filesystem is\n"
285 "unmounted or mounted "
288 if (sb
->s_feature_incompat
&
289 EXT3_FEATURE_INCOMPAT_RECOVER
) {
290 bb_error_msg_and_die(
291 "The needs_recovery flag is set. "
292 "%s before clearing the has_journal flag.",
295 if (sb
->s_journal_inum
) {
296 remove_journal_inode(fs
);
298 if (sb
->s_journal_dev
) {
299 remove_journal_device(fs
);
302 if (journal
&& !old_journal
) {
304 * If adding a journal flag, let the create journal
305 * code below handle creating setting the flag and
306 * creating the journal. We supply a default size if
311 sb
->s_feature_compat
&= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
313 if (dxdir
&& !old_dxdir
) {
314 if (!sb
->s_def_hash_version
)
315 sb
->s_def_hash_version
= EXT2_HASH_TEA
;
316 if (uuid_is_null((unsigned char *) sb
->s_hash_seed
))
317 generate_uuid((unsigned char *) sb
->s_hash_seed
);
320 if (sb
->s_rev_level
== EXT2_GOOD_OLD_REV
&&
321 (sb
->s_feature_compat
|| sb
->s_feature_ro_compat
||
322 sb
->s_feature_incompat
))
323 ext2fs_update_dynamic_rev(fs
);
324 if ((sparse
!= old_sparse
) ||
325 (filetype
!= old_filetype
)) {
326 sb
->s_state
&= ~EXT2_VALID_FS
;
327 printf("\n%s\n", please_fsck
);
329 if ((old_compat
!= sb
->s_feature_compat
) ||
330 (old_ro_compat
!= sb
->s_feature_ro_compat
) ||
331 (old_incompat
!= sb
->s_feature_incompat
))
332 ext2fs_mark_super_dirty(fs
);
336 * Add a journal to the filesystem.
338 static void add_journal(ext2_filsys fs
)
340 if (fs
->super
->s_feature_compat
&
341 EXT3_FEATURE_COMPAT_HAS_JOURNAL
) {
342 bb_error_msg_and_die("The filesystem already has a journal");
344 if (journal_device
) {
345 make_journal_device(journal_device
, fs
, 0, 0);
346 } else if (journal_size
) {
347 make_journal_blocks(fs
, journal_size
, journal_flags
, 0);
349 * If the filesystem wasn't mounted, we need to force
350 * the block group descriptors out.
352 if ((mount_flags
& EXT2_MF_MOUNTED
) == 0)
353 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
355 print_check_message(fs
);
361 static char * x_blkid_get_devname(const char *token
)
363 char *dev_name
= (char *)token
;
365 if (resolve_mount_spec(&dev_name
) != 1 || !dev_name
)
366 bb_error_msg_and_die("Unable to resolve '%s'", token
);
370 #ifdef CONFIG_E2LABEL
371 static void parse_e2label_options(int argc
, char ** argv
)
373 if ((argc
< 2) || (argc
> 3))
375 io_options
= strchr(argv
[1], '?');
378 device_name
= x_blkid_get_devname(argv
[1]);
380 open_flag
= EXT2_FLAG_RW
| EXT2_FLAG_JOURNAL_DEV_OK
;
387 #define parse_e2label_options(x,y)
390 static time_t parse_time(char *str
)
394 if (strcmp(str
, "now") == 0) {
397 memset(&ts
, 0, sizeof(ts
));
399 strptime(str
, "%Y%m%d%H%M%S", &ts
);
401 sscanf(str
, "%4d%2d%2d%2d%2d%2d", &ts
.tm_year
, &ts
.tm_mon
,
402 &ts
.tm_mday
, &ts
.tm_hour
, &ts
.tm_min
, &ts
.tm_sec
);
405 if (ts
.tm_year
< 0 || ts
.tm_mon
< 0 || ts
.tm_mon
> 11 ||
406 ts
.tm_mday
< 0 || ts
.tm_mday
> 31 || ts
.tm_hour
> 23 ||
407 ts
.tm_min
> 59 || ts
.tm_sec
> 61)
410 if (ts
.tm_mday
== 0) {
411 bb_error_msg_and_die("can't parse date/time specifier: %s", str
);
416 static void parse_tune2fs_options(int argc
, char **argv
)
421 printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION
, E2FSPROGS_DATE
);
422 while ((c
= getopt(argc
, argv
, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF
)
426 max_mount_count
= xatou_range(optarg
, 0, 16000);
427 if (max_mount_count
== 0)
428 max_mount_count
= -1;
430 open_flag
= EXT2_FLAG_RW
;
433 mount_count
= xatou_range(optarg
, 0, 16000);
435 open_flag
= EXT2_FLAG_RW
;
438 if (strcmp (optarg
, "continue") == 0)
439 errors
= EXT2_ERRORS_CONTINUE
;
440 else if (strcmp (optarg
, "remount-ro") == 0)
441 errors
= EXT2_ERRORS_RO
;
442 else if (strcmp (optarg
, "panic") == 0)
443 errors
= EXT2_ERRORS_PANIC
;
445 bb_error_msg_and_die("bad error behavior - %s", optarg
);
448 open_flag
= EXT2_FLAG_RW
;
450 case 'f': /* Force */
454 resgid
= bb_strtoul(optarg
, NULL
, 10);
456 resgid
= xgroup2gid(optarg
);
458 open_flag
= EXT2_FLAG_RW
;
461 interval
= strtoul(optarg
, &tmp
, 0);
474 case 'M': /* months! */
475 interval
*= 86400 * 30;
479 case 'W': /* weeks */
480 interval
*= 86400 * 7;
484 if (*tmp
|| interval
> (365 * 86400)) {
485 bb_error_msg_and_die("bad interval - %s", optarg
);
488 open_flag
= EXT2_FLAG_RW
;
493 open_flag
= EXT2_FLAG_RW
;
496 parse_journal_opts(&journal_device
, &journal_flags
, &journal_size
, optarg
);
497 open_flag
= EXT2_FLAG_RW
;
505 open_flag
= EXT2_FLAG_RW
|
506 EXT2_FLAG_JOURNAL_DEV_OK
;
509 reserved_ratio
= xatou_range(optarg
, 0, 50);
511 open_flag
= EXT2_FLAG_RW
;
514 new_last_mounted
= optarg
;
516 open_flag
= EXT2_FLAG_RW
;
520 bb_error_msg_and_die("-o may only be specified once");
522 mntopts_cmd
= optarg
;
523 open_flag
= EXT2_FLAG_RW
;
528 bb_error_msg_and_die("-O may only be specified once");
530 features_cmd
= optarg
;
531 open_flag
= EXT2_FLAG_RW
;
534 reserved_blocks
= xatoul(optarg
);
536 open_flag
= EXT2_FLAG_RW
;
539 s_flag
= atoi(optarg
);
540 open_flag
= EXT2_FLAG_RW
;
544 last_check_time
= parse_time(optarg
);
545 open_flag
= EXT2_FLAG_RW
;
548 resuid
= bb_strtoul(optarg
, NULL
, 10);
550 resuid
= xuname2uid(optarg
);
552 open_flag
= EXT2_FLAG_RW
;
557 open_flag
= EXT2_FLAG_RW
|
558 EXT2_FLAG_JOURNAL_DEV_OK
;
563 if (optind
< argc
- 1 || optind
== argc
)
565 if (!open_flag
&& !l_flag
)
567 io_options
= strchr(argv
[optind
], '?');
570 device_name
= x_blkid_get_devname(argv
[optind
]);
573 static void tune2fs_clean_up(void)
575 if (ENABLE_FEATURE_CLEAN_UP
&& device_name
) free(device_name
);
576 if (ENABLE_FEATURE_CLEAN_UP
&& journal_device
) free(journal_device
);
579 int tune2fs_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
580 int tune2fs_main(int argc
, char **argv
)
584 struct ext2_super_block
*sb
;
587 if (ENABLE_FEATURE_CLEAN_UP
)
588 atexit(tune2fs_clean_up
);
590 if (ENABLE_E2LABEL
&& (applet_name
[0] == 'e')) /* e2label */
591 parse_e2label_options(argc
, argv
);
593 parse_tune2fs_options(argc
, argv
); /* tune2fs */
595 io_ptr
= unix_io_manager
;
596 retval
= ext2fs_open2(device_name
, io_options
, open_flag
,
599 bb_error_msg_and_die("No valid superblock on %s", device_name
);
602 /* For e2label emulation */
603 printf("%.*s\n", (int) sizeof(sb
->s_volume_name
),
607 retval
= ext2fs_check_if_mounted(device_name
, &mount_flags
);
609 bb_error_msg_and_die("can't determine if %s is mounted", device_name
);
610 /* Normally we only need to write out the superblock */
611 fs
->flags
|= EXT2_FLAG_SUPER_ONLY
;
614 sb
->s_max_mnt_count
= max_mount_count
;
615 ext2fs_mark_super_dirty(fs
);
616 printf("Setting maximal mount count to %d\n", max_mount_count
);
619 sb
->s_mnt_count
= mount_count
;
620 ext2fs_mark_super_dirty(fs
);
621 printf("Setting current mount count to %d\n", mount_count
);
624 sb
->s_errors
= errors
;
625 ext2fs_mark_super_dirty(fs
);
626 printf("Setting error behavior to %d\n", errors
);
629 sb
->s_def_resgid
= resgid
;
630 ext2fs_mark_super_dirty(fs
);
631 printf("Setting reserved blocks gid to %lu\n", resgid
);
634 sb
->s_checkinterval
= interval
;
635 ext2fs_mark_super_dirty(fs
);
636 printf("Setting interval between check %lu seconds\n", interval
);
639 sb
->s_r_blocks_count
= (sb
->s_blocks_count
/ 100)
641 ext2fs_mark_super_dirty(fs
);
642 printf("Setting reserved blocks percentage to %u (%u blocks)\n",
643 reserved_ratio
, sb
->s_r_blocks_count
);
646 if (reserved_blocks
>= sb
->s_blocks_count
/2)
647 bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks
);
648 sb
->s_r_blocks_count
= reserved_blocks
;
649 ext2fs_mark_super_dirty(fs
);
650 printf("Setting reserved blocks count to %lu\n", reserved_blocks
);
653 if (sb
->s_feature_ro_compat
&
654 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
)
655 bb_error_msg("\nThe filesystem already has sparse superblocks");
657 sb
->s_feature_ro_compat
|=
658 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
659 sb
->s_state
&= ~EXT2_VALID_FS
;
660 ext2fs_mark_super_dirty(fs
);
661 printf("\nSparse superblock flag set. %s", please_fsck
);
665 if (!(sb
->s_feature_ro_compat
&
666 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
))
667 bb_error_msg("\nThe filesystem already has sparse superblocks disabled");
669 sb
->s_feature_ro_compat
&=
670 ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
671 sb
->s_state
&= ~EXT2_VALID_FS
;
672 fs
->flags
|= EXT2_FLAG_MASTER_SB_ONLY
;
673 ext2fs_mark_super_dirty(fs
);
674 printf("\nSparse superblock flag cleared. %s", please_fsck
);
678 sb
->s_lastcheck
= last_check_time
;
679 ext2fs_mark_super_dirty(fs
);
680 printf("Setting time filesystem last checked to %s\n",
681 ctime(&last_check_time
));
684 sb
->s_def_resuid
= resuid
;
685 ext2fs_mark_super_dirty(fs
);
686 printf("Setting reserved blocks uid to %lu\n", resuid
);
689 if (strlen(new_label
) > sizeof(sb
->s_volume_name
))
690 bb_error_msg("Warning: label too long, truncating");
691 memset(sb
->s_volume_name
, 0, sizeof(sb
->s_volume_name
));
692 safe_strncpy(sb
->s_volume_name
, new_label
,
693 sizeof(sb
->s_volume_name
));
694 ext2fs_mark_super_dirty(fs
);
697 memset(sb
->s_last_mounted
, 0, sizeof(sb
->s_last_mounted
));
698 safe_strncpy(sb
->s_last_mounted
, new_last_mounted
,
699 sizeof(sb
->s_last_mounted
));
700 ext2fs_mark_super_dirty(fs
);
703 update_mntopts(fs
, mntopts_cmd
);
705 update_feature_set(fs
, features_cmd
);
706 if (journal_size
|| journal_device
)
710 if ((strcasecmp(new_UUID
, "null") == 0) ||
711 (strcasecmp(new_UUID
, "clear") == 0)) {
712 uuid_clear(sb
->s_uuid
);
714 } else if (strcasecmp(new_UUID, "time") == 0) {
715 uuid_generate_time(sb->s_uuid);
717 } else if (strcasecmp(new_UUID
, "random") == 0) {
718 generate_uuid(sb
->s_uuid
);
719 } else if (parse_uuid(new_UUID
, sb
->s_uuid
)) {
720 bb_error_msg_and_die("Invalid UUID format");
722 ext2fs_mark_super_dirty(fs
);
727 return (ext2fs_close (fs
) ? 1 : 0);