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] "
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"
32 * 93/10/31 - Added the -c option to change the maximal mount counts
33 * 93/12/14 - Added -l flag to list contents of superblock
34 * M.J.E. Mol (marcel@duteca.et.tudelft.nl)
35 * F.W. ten Wolde (franky@duteca.et.tudelft.nl)
36 * 93/12/29 - Added the -e option to change errors behavior
37 * 94/02/27 - Ported to use the ext2fs library
38 * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
41 #include <sys/types.h>
51 #include "ext2fs/ext2_fs.h"
52 #include "ext2fs/ext2fs.h"
55 #include "ext2fs/kernel-jbd.h"
57 #include "volume_id.h"
61 static char * device_name
= NULL
;
62 static char * new_label
, *new_last_mounted
, *new_UUID
;
63 static char * io_options
;
64 static int c_flag
, C_flag
, e_flag
, f_flag
, g_flag
, i_flag
, l_flag
, L_flag
;
65 static int m_flag
, M_flag
, r_flag
, s_flag
= -1, u_flag
, U_flag
, T_flag
;
66 static time_t last_check_time
;
67 static int print_label
;
68 static int max_mount_count
, mount_count
, mount_flags
;
69 static unsigned long interval
, reserved_blocks
;
70 static unsigned reserved_ratio
;
71 static unsigned long resgid
, resuid
;
72 static unsigned short errors
;
74 static char *features_cmd
;
75 static char *mntopts_cmd
;
77 static int journal_size
, journal_flags
;
78 static char *journal_device
= NULL
;
80 static const char *please_fsck
= "Please run e2fsck on the filesystem\n";
82 static __u32 ok_features
[3] = {
83 EXT3_FEATURE_COMPAT_HAS_JOURNAL
| EXT2_FEATURE_COMPAT_DIR_INDEX
,
84 EXT2_FEATURE_INCOMPAT_FILETYPE
,
85 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
89 * Remove an external journal from the filesystem
91 static void remove_journal_device(ext2_filsys fs
)
96 journal_superblock_t
*jsb
;
99 int commit_remove_journal
= 0;
103 commit_remove_journal
= 1; /* force removal even if error */
105 unparse_uuid(fs
->super
->s_journal_uuid
, buf
);
106 journal_path
= get_devname_from_uuid(buf
);
110 ext2fs_find_block_device(fs
->super
->s_journal_dev
);
115 io_ptr
= unix_io_manager
;
116 retval
= ext2fs_open(journal_path
, EXT2_FLAG_RW
|
117 EXT2_FLAG_JOURNAL_DEV_OK
, 0,
118 fs
->blocksize
, io_ptr
, &jfs
);
120 bb_error_msg("Failed to open external journal");
121 goto no_valid_journal
;
123 if (!(jfs
->super
->s_feature_incompat
& EXT3_FEATURE_INCOMPAT_JOURNAL_DEV
)) {
124 bb_error_msg("%s is not a journal device", journal_path
);
125 goto no_valid_journal
;
128 /* Get the journal superblock */
129 if ((retval
= io_channel_read_blk(jfs
->io
, 1, -1024, buf
))) {
130 bb_error_msg("Failed to read journal superblock");
131 goto no_valid_journal
;
134 jsb
= (journal_superblock_t
*) buf
;
135 if ((jsb
->s_header
.h_magic
!= (unsigned) ntohl(JFS_MAGIC_NUMBER
)) ||
136 (jsb
->s_header
.h_blocktype
!= (unsigned) ntohl(JFS_SUPERBLOCK_V2
))) {
137 bb_error_msg("Journal superblock not found!");
138 goto no_valid_journal
;
141 /* Find the filesystem UUID */
142 nr_users
= ntohl(jsb
->s_nr_users
);
143 for (i
=0; i
< nr_users
; i
++) {
144 if (memcmp(fs
->super
->s_uuid
,
145 &jsb
->s_users
[i
*16], 16) == 0)
149 bb_error_msg("Filesystem's UUID not found on journal device");
150 commit_remove_journal
= 1;
151 goto no_valid_journal
;
154 for (i
=0; i
< nr_users
; i
++)
155 memcpy(&jsb
->s_users
[i
*16], &jsb
->s_users
[(i
+1)*16], 16);
156 jsb
->s_nr_users
= htonl(nr_users
);
158 /* Write back the journal superblock */
159 if ((retval
= io_channel_write_blk(jfs
->io
, 1, -1024, buf
))) {
160 bb_error_msg("Failed to write journal superblock");
161 goto no_valid_journal
;
164 commit_remove_journal
= 1;
167 if (commit_remove_journal
== 0)
168 bb_error_msg_and_die("Journal NOT removed");
169 fs
->super
->s_journal_dev
= 0;
170 uuid_clear(fs
->super
->s_journal_uuid
);
171 ext2fs_mark_super_dirty(fs
);
172 puts("Journal removed");
176 /* Helper function for remove_journal_inode */
177 static int release_blocks_proc(ext2_filsys fs
, blk_t
*blocknr
,
178 int blockcnt
EXT2FS_ATTR((unused
)),
179 void *private EXT2FS_ATTR((unused
)))
185 ext2fs_unmark_block_bitmap(fs
->block_map
,block
);
186 group
= ext2fs_group_of_blk(fs
, block
);
187 fs
->group_desc
[group
].bg_free_blocks_count
++;
188 fs
->super
->s_free_blocks_count
++;
193 * Remove the journal inode from the filesystem
195 static void remove_journal_inode(ext2_filsys fs
)
197 struct ext2_inode inode
;
199 ino_t ino
= fs
->super
->s_journal_inum
;
200 const char *msg
= "to read";
201 const char *s
= "journal inode";
203 retval
= ext2fs_read_inode(fs
, ino
, &inode
);
205 goto REMOVE_JOURNAL_INODE_ERROR
;
206 if (ino
== EXT2_JOURNAL_INO
) {
207 retval
= ext2fs_read_bitmaps(fs
);
209 msg
= "to read bitmaps";
211 goto REMOVE_JOURNAL_INODE_ERROR
;
213 retval
= ext2fs_block_iterate(fs
, ino
, 0, NULL
,
214 release_blocks_proc
, NULL
);
217 goto REMOVE_JOURNAL_INODE_ERROR
;
219 memset(&inode
, 0, sizeof(inode
));
220 ext2fs_mark_bb_dirty(fs
);
221 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
223 inode
.i_flags
&= ~EXT2_IMMUTABLE_FL
;
224 retval
= ext2fs_write_inode(fs
, ino
, &inode
);
227 REMOVE_JOURNAL_INODE_ERROR
:
228 bb_error_msg_and_die("Failed %s %s", msg
, s
);
230 fs
->super
->s_journal_inum
= 0;
231 ext2fs_mark_super_dirty(fs
);
235 * Update the default mount options
237 static void update_mntopts(ext2_filsys fs
, char *mntopts
)
239 struct ext2_super_block
*sb
= fs
->super
;
241 if (e2p_edit_mntopts(mntopts
, &sb
->s_default_mount_opts
, ~0))
242 bb_error_msg_and_die("Invalid mount option set: %s", mntopts
);
243 ext2fs_mark_super_dirty(fs
);
247 * Update the feature set as provided by the user.
249 static void update_feature_set(ext2_filsys fs
, char *features
)
251 int sparse
, old_sparse
, filetype
, old_filetype
;
252 int journal
, old_journal
, dxdir
, old_dxdir
;
253 struct ext2_super_block
*sb
= fs
->super
;
254 __u32 old_compat
, old_incompat
, old_ro_compat
;
256 old_compat
= sb
->s_feature_compat
;
257 old_ro_compat
= sb
->s_feature_ro_compat
;
258 old_incompat
= sb
->s_feature_incompat
;
260 old_sparse
= sb
->s_feature_ro_compat
&
261 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
262 old_filetype
= sb
->s_feature_incompat
&
263 EXT2_FEATURE_INCOMPAT_FILETYPE
;
264 old_journal
= sb
->s_feature_compat
&
265 EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
266 old_dxdir
= sb
->s_feature_compat
&
267 EXT2_FEATURE_COMPAT_DIR_INDEX
;
268 if (e2p_edit_feature(features
, &sb
->s_feature_compat
, ok_features
))
269 bb_error_msg_and_die("Invalid filesystem option set: %s", features
);
270 sparse
= sb
->s_feature_ro_compat
&
271 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
272 filetype
= sb
->s_feature_incompat
&
273 EXT2_FEATURE_INCOMPAT_FILETYPE
;
274 journal
= sb
->s_feature_compat
&
275 EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
276 dxdir
= sb
->s_feature_compat
&
277 EXT2_FEATURE_COMPAT_DIR_INDEX
;
278 if (old_journal
&& !journal
) {
279 if ((mount_flags
& EXT2_MF_MOUNTED
) &&
280 !(mount_flags
& EXT2_MF_READONLY
)) {
281 bb_error_msg_and_die(
282 "The has_journal flag may only be "
283 "cleared when the filesystem is\n"
284 "unmounted or mounted "
287 if (sb
->s_feature_incompat
&
288 EXT3_FEATURE_INCOMPAT_RECOVER
) {
289 bb_error_msg_and_die(
290 "The needs_recovery flag is set. "
291 "%s before clearing the has_journal flag.",
294 if (sb
->s_journal_inum
) {
295 remove_journal_inode(fs
);
297 if (sb
->s_journal_dev
) {
298 remove_journal_device(fs
);
301 if (journal
&& !old_journal
) {
303 * If adding a journal flag, let the create journal
304 * code below handle creating setting the flag and
305 * creating the journal. We supply a default size if
310 sb
->s_feature_compat
&= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL
;
312 if (dxdir
&& !old_dxdir
) {
313 if (!sb
->s_def_hash_version
)
314 sb
->s_def_hash_version
= EXT2_HASH_TEA
;
315 if (uuid_is_null((unsigned char *) sb
->s_hash_seed
))
316 generate_uuid((unsigned char *) sb
->s_hash_seed
);
319 if (sb
->s_rev_level
== EXT2_GOOD_OLD_REV
&&
320 (sb
->s_feature_compat
|| sb
->s_feature_ro_compat
||
321 sb
->s_feature_incompat
))
322 ext2fs_update_dynamic_rev(fs
);
323 if ((sparse
!= old_sparse
) ||
324 (filetype
!= old_filetype
)) {
325 sb
->s_state
&= ~EXT2_VALID_FS
;
326 printf("\n%s\n", please_fsck
);
328 if ((old_compat
!= sb
->s_feature_compat
) ||
329 (old_ro_compat
!= sb
->s_feature_ro_compat
) ||
330 (old_incompat
!= sb
->s_feature_incompat
))
331 ext2fs_mark_super_dirty(fs
);
335 * Add a journal to the filesystem.
337 static void add_journal(ext2_filsys fs
)
339 if (fs
->super
->s_feature_compat
&
340 EXT3_FEATURE_COMPAT_HAS_JOURNAL
) {
341 bb_error_msg_and_die("The filesystem already has a journal");
343 if (journal_device
) {
344 make_journal_device(journal_device
, fs
, 0, 0);
345 } else if (journal_size
) {
346 make_journal_blocks(fs
, journal_size
, journal_flags
, 0);
348 * If the filesystem wasn't mounted, we need to force
349 * the block group descriptors out.
351 if ((mount_flags
& EXT2_MF_MOUNTED
) == 0)
352 fs
->flags
&= ~EXT2_FLAG_SUPER_ONLY
;
354 print_check_message(fs
);
360 static char * x_blkid_get_devname(const char *token
)
362 char *dev_name
= (char *)token
;
364 if (resolve_mount_spec(&dev_name
) != 1 || !dev_name
)
365 bb_error_msg_and_die("Unable to resolve '%s'", token
);
369 #ifdef CONFIG_E2LABEL
370 static void parse_e2label_options(int argc
, char ** argv
)
372 if ((argc
< 2) || (argc
> 3))
374 io_options
= strchr(argv
[1], '?');
377 device_name
= x_blkid_get_devname(argv
[1]);
379 open_flag
= EXT2_FLAG_RW
| EXT2_FLAG_JOURNAL_DEV_OK
;
386 #define parse_e2label_options(x,y)
389 static time_t parse_time(char *str
)
393 if (strcmp(str
, "now") == 0) {
396 memset(&ts
, 0, sizeof(ts
));
398 strptime(str
, "%Y%m%d%H%M%S", &ts
);
400 sscanf(str
, "%4d%2d%2d%2d%2d%2d", &ts
.tm_year
, &ts
.tm_mon
,
401 &ts
.tm_mday
, &ts
.tm_hour
, &ts
.tm_min
, &ts
.tm_sec
);
404 if (ts
.tm_year
< 0 || ts
.tm_mon
< 0 || ts
.tm_mon
> 11 ||
405 ts
.tm_mday
< 0 || ts
.tm_mday
> 31 || ts
.tm_hour
> 23 ||
406 ts
.tm_min
> 59 || ts
.tm_sec
> 61)
409 if (ts
.tm_mday
== 0) {
410 bb_error_msg_and_die("can't parse date/time specifier: %s", str
);
415 static void parse_tune2fs_options(int argc
, char **argv
)
420 printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION
, E2FSPROGS_DATE
);
421 while ((c
= getopt(argc
, argv
, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF
)
425 max_mount_count
= xatou_range(optarg
, 0, 16000);
426 if (max_mount_count
== 0)
427 max_mount_count
= -1;
429 open_flag
= EXT2_FLAG_RW
;
432 mount_count
= xatou_range(optarg
, 0, 16000);
434 open_flag
= EXT2_FLAG_RW
;
437 if (strcmp (optarg
, "continue") == 0)
438 errors
= EXT2_ERRORS_CONTINUE
;
439 else if (strcmp (optarg
, "remount-ro") == 0)
440 errors
= EXT2_ERRORS_RO
;
441 else if (strcmp (optarg
, "panic") == 0)
442 errors
= EXT2_ERRORS_PANIC
;
444 bb_error_msg_and_die("bad error behavior - %s", optarg
);
447 open_flag
= EXT2_FLAG_RW
;
449 case 'f': /* Force */
453 resgid
= bb_strtoul(optarg
, NULL
, 10);
455 resgid
= xgroup2gid(optarg
);
457 open_flag
= EXT2_FLAG_RW
;
460 interval
= strtoul(optarg
, &tmp
, 0);
473 case 'M': /* months! */
474 interval
*= 86400 * 30;
478 case 'W': /* weeks */
479 interval
*= 86400 * 7;
483 if (*tmp
|| interval
> (365 * 86400)) {
484 bb_error_msg_and_die("bad interval - %s", optarg
);
487 open_flag
= EXT2_FLAG_RW
;
492 open_flag
= EXT2_FLAG_RW
;
495 parse_journal_opts(&journal_device
, &journal_flags
, &journal_size
, optarg
);
496 open_flag
= EXT2_FLAG_RW
;
504 open_flag
= EXT2_FLAG_RW
|
505 EXT2_FLAG_JOURNAL_DEV_OK
;
508 reserved_ratio
= xatou_range(optarg
, 0, 50);
510 open_flag
= EXT2_FLAG_RW
;
513 new_last_mounted
= optarg
;
515 open_flag
= EXT2_FLAG_RW
;
519 bb_error_msg_and_die("-o may only be specified once");
521 mntopts_cmd
= optarg
;
522 open_flag
= EXT2_FLAG_RW
;
527 bb_error_msg_and_die("-O may only be specified once");
529 features_cmd
= optarg
;
530 open_flag
= EXT2_FLAG_RW
;
533 reserved_blocks
= xatoul(optarg
);
535 open_flag
= EXT2_FLAG_RW
;
538 s_flag
= atoi(optarg
);
539 open_flag
= EXT2_FLAG_RW
;
543 last_check_time
= parse_time(optarg
);
544 open_flag
= EXT2_FLAG_RW
;
547 resuid
= bb_strtoul(optarg
, NULL
, 10);
549 resuid
= xuname2uid(optarg
);
551 open_flag
= EXT2_FLAG_RW
;
556 open_flag
= EXT2_FLAG_RW
|
557 EXT2_FLAG_JOURNAL_DEV_OK
;
562 if (optind
< argc
- 1 || optind
== argc
)
564 if (!open_flag
&& !l_flag
)
566 io_options
= strchr(argv
[optind
], '?');
569 device_name
= x_blkid_get_devname(argv
[optind
]);
572 static void tune2fs_clean_up(void)
574 if (ENABLE_FEATURE_CLEAN_UP
&& device_name
) free(device_name
);
575 if (ENABLE_FEATURE_CLEAN_UP
&& journal_device
) free(journal_device
);
578 int tune2fs_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
579 int tune2fs_main(int argc
, char **argv
)
583 struct ext2_super_block
*sb
;
586 if (ENABLE_FEATURE_CLEAN_UP
)
587 atexit(tune2fs_clean_up
);
589 if (ENABLE_E2LABEL
&& (applet_name
[0] == 'e')) /* e2label */
590 parse_e2label_options(argc
, argv
);
592 parse_tune2fs_options(argc
, argv
); /* tune2fs */
594 io_ptr
= unix_io_manager
;
595 retval
= ext2fs_open2(device_name
, io_options
, open_flag
,
598 bb_error_msg_and_die("No valid superblock on %s", device_name
);
601 /* For e2label emulation */
602 printf("%.*s\n", (int) sizeof(sb
->s_volume_name
),
606 retval
= ext2fs_check_if_mounted(device_name
, &mount_flags
);
608 bb_error_msg_and_die("can't determine if %s is mounted", device_name
);
609 /* Normally we only need to write out the superblock */
610 fs
->flags
|= EXT2_FLAG_SUPER_ONLY
;
613 sb
->s_max_mnt_count
= max_mount_count
;
614 ext2fs_mark_super_dirty(fs
);
615 printf("Setting maximal mount count to %d\n", max_mount_count
);
618 sb
->s_mnt_count
= mount_count
;
619 ext2fs_mark_super_dirty(fs
);
620 printf("Setting current mount count to %d\n", mount_count
);
623 sb
->s_errors
= errors
;
624 ext2fs_mark_super_dirty(fs
);
625 printf("Setting error behavior to %d\n", errors
);
628 sb
->s_def_resgid
= resgid
;
629 ext2fs_mark_super_dirty(fs
);
630 printf("Setting reserved blocks gid to %lu\n", resgid
);
633 sb
->s_checkinterval
= interval
;
634 ext2fs_mark_super_dirty(fs
);
635 printf("Setting interval between check %lu seconds\n", interval
);
638 sb
->s_r_blocks_count
= (sb
->s_blocks_count
/ 100)
640 ext2fs_mark_super_dirty(fs
);
641 printf("Setting reserved blocks percentage to %u (%u blocks)\n",
642 reserved_ratio
, sb
->s_r_blocks_count
);
645 if (reserved_blocks
>= sb
->s_blocks_count
/2)
646 bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks
);
647 sb
->s_r_blocks_count
= reserved_blocks
;
648 ext2fs_mark_super_dirty(fs
);
649 printf("Setting reserved blocks count to %lu\n", reserved_blocks
);
652 if (sb
->s_feature_ro_compat
&
653 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
)
654 bb_error_msg("\nThe filesystem already has sparse superblocks");
656 sb
->s_feature_ro_compat
|=
657 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
658 sb
->s_state
&= ~EXT2_VALID_FS
;
659 ext2fs_mark_super_dirty(fs
);
660 printf("\nSparse superblock flag set. %s", please_fsck
);
664 if (!(sb
->s_feature_ro_compat
&
665 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
))
666 bb_error_msg("\nThe filesystem already has sparse superblocks disabled");
668 sb
->s_feature_ro_compat
&=
669 ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
;
670 sb
->s_state
&= ~EXT2_VALID_FS
;
671 fs
->flags
|= EXT2_FLAG_MASTER_SB_ONLY
;
672 ext2fs_mark_super_dirty(fs
);
673 printf("\nSparse superblock flag cleared. %s", please_fsck
);
677 sb
->s_lastcheck
= last_check_time
;
678 ext2fs_mark_super_dirty(fs
);
679 printf("Setting time filesystem last checked to %s\n",
680 ctime(&last_check_time
));
683 sb
->s_def_resuid
= resuid
;
684 ext2fs_mark_super_dirty(fs
);
685 printf("Setting reserved blocks uid to %lu\n", resuid
);
688 if (strlen(new_label
) > sizeof(sb
->s_volume_name
))
689 bb_error_msg("Warning: label too long, truncating");
690 memset(sb
->s_volume_name
, 0, sizeof(sb
->s_volume_name
));
691 safe_strncpy(sb
->s_volume_name
, new_label
,
692 sizeof(sb
->s_volume_name
));
693 ext2fs_mark_super_dirty(fs
);
696 memset(sb
->s_last_mounted
, 0, sizeof(sb
->s_last_mounted
));
697 safe_strncpy(sb
->s_last_mounted
, new_last_mounted
,
698 sizeof(sb
->s_last_mounted
));
699 ext2fs_mark_super_dirty(fs
);
702 update_mntopts(fs
, mntopts_cmd
);
704 update_feature_set(fs
, features_cmd
);
705 if (journal_size
|| journal_device
)
709 if ((strcasecmp(new_UUID
, "null") == 0) ||
710 (strcasecmp(new_UUID
, "clear") == 0)) {
711 uuid_clear(sb
->s_uuid
);
713 } else if (strcasecmp(new_UUID, "time") == 0) {
714 uuid_generate_time(sb->s_uuid);
716 } else if (strcasecmp(new_UUID
, "random") == 0) {
717 generate_uuid(sb
->s_uuid
);
718 } else if (parse_uuid(new_UUID
, sb
->s_uuid
)) {
719 bb_error_msg_and_die("Invalid UUID format");
721 ext2fs_mark_super_dirty(fs
);
726 return (ext2fs_close (fs
) ? 1 : 0);