X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=misc%2Ftune2fs.c;h=e48e641e6a3ca28fe2bad85f6018e40dd0ba875d;hb=f21c6a5134cedb9ae50917c2ae3ebde04e3dff94;hp=346e2d17c87fe42c16228f9527a148aa4d8b762e;hpb=c3ecabe61d075efa1b3f451964f85f3157307f8d;p=tools%2Fe2fsprogs.git diff --git a/misc/tune2fs.c b/misc/tune2fs.c index 346e2d1..e48e641 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -26,7 +26,7 @@ */ #define _XOPEN_SOURCE 600 /* for inclusion of strptime() */ -#define _BSD_SOURCE /* for inclusion of strcasecmp() */ +#include "config.h" #include #include #ifdef HAVE_GETOPT_H @@ -40,6 +40,11 @@ extern int optind; #ifdef HAVE_STDLIB_H #include #endif +#ifdef HAVE_STRINGS_H +#include /* for strcasecmp() */ +#else +#define _BSD_SOURCE /* for inclusion of strcasecmp() via */ +#endif #include #include #include @@ -55,7 +60,7 @@ extern int optind; #include "jfs_user.h" #include "util.h" #include "blkid/blkid.h" -#include "quota/mkquota.h" +#include "quota/quotaio.h" #include "../version.h" #include "nls-enable.h" @@ -72,6 +77,7 @@ char *io_options; static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; static int I_flag; +static int clear_mmp; static time_t last_check_time; static int print_label; static int max_mount_count, mount_count, mount_flags; @@ -92,6 +98,7 @@ static int usrquota, grpquota; int journal_size, journal_flags; char *journal_device; +static blk64_t journal_location = ~0LL; static struct list_head blk_move_list; @@ -115,10 +122,13 @@ static void usage(void) "[-g group]\n" "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n" "\t[-m reserved_blocks_percent] " - "[-o [^]mount_options[,...]] \n" + "[-o [^]mount_options[,...]] [-p mmp_update_interval]\n" "\t[-r reserved_blocks_count] [-u user] [-C mount_count] " "[-L volume_label]\n" "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n" +#ifdef CONFIG_QUOTA + "\t[-Q quota_options]\n" +#endif "\t[-E extended-option[,...]] [-T last_check_time] " "[-U UUID]\n\t[ -I new_inode_size ] device\n"), program_name); exit(1); @@ -131,15 +141,20 @@ static __u32 ok_features[3] = { /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_EXTENTS | - EXT4_FEATURE_INCOMPAT_FLEX_BG, + EXT4_FEATURE_INCOMPAT_FLEX_BG | + EXT4_FEATURE_INCOMPAT_EA_INODE| + EXT4_FEATURE_INCOMPAT_MMP| + EXT4_FEATURE_INCOMPAT_DIRDATA, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| EXT4_FEATURE_RO_COMPAT_GDT_CSUM | - EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | - EXT4_FEATURE_RO_COMPAT_QUOTA +#ifdef CONFIG_QUOTA + EXT4_FEATURE_RO_COMPAT_QUOTA | +#endif + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER }; static __u32 clear_ok_features[3] = { @@ -149,24 +164,72 @@ static __u32 clear_ok_features[3] = { EXT2_FEATURE_COMPAT_DIR_INDEX, /* Incompat */ EXT2_FEATURE_INCOMPAT_FILETYPE | - EXT4_FEATURE_INCOMPAT_FLEX_BG, + EXT4_FEATURE_INCOMPAT_FLEX_BG | + EXT4_FEATURE_INCOMPAT_EA_INODE| + EXT4_FEATURE_INCOMPAT_MMP| + EXT4_FEATURE_INCOMPAT_DIRDATA, /* R/O compat */ EXT2_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| EXT4_FEATURE_RO_COMPAT_DIR_NLINK| EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| - EXT4_FEATURE_RO_COMPAT_GDT_CSUM | - EXT4_FEATURE_RO_COMPAT_QUOTA +#ifdef CONFIG_QUOTA + EXT4_FEATURE_RO_COMPAT_QUOTA | +#endif + EXT4_FEATURE_RO_COMPAT_GDT_CSUM }; +/** + * Try to get journal super block if any + */ +static int get_journal_sb(ext2_filsys jfs, char buf[SUPERBLOCK_SIZE]) +{ + int retval; + journal_superblock_t *jsb; + + if (!(jfs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + return EXT2_ET_UNSUPP_FEATURE; + } + + /* Get the journal superblock */ + if ((retval = io_channel_read_blk64(jfs->io, + ext2fs_journal_sb_start(jfs->blocksize), -SUPERBLOCK_SIZE, buf))) { + com_err(program_name, retval, "%s", + _("while reading journal superblock")); + return retval; + } + + jsb = (journal_superblock_t *) buf; + if ((jsb->s_header.h_magic != (unsigned)ntohl(JFS_MAGIC_NUMBER)) || + (jsb->s_header.h_blocktype != (unsigned)ntohl(JFS_SUPERBLOCK_V2))) { + fputs(_("Journal superblock not found!\n"), stderr); + return EXT2_ET_BAD_MAGIC; + } + + return 0; +} + +static __u8 *journal_user(__u8 uuid[UUID_SIZE], __u8 s_users[JFS_USERS_SIZE], + int nr_users) +{ + int i; + for (i = 0; i < nr_users; i++) { + if (memcmp(uuid, &s_users[i * UUID_SIZE], UUID_SIZE) == 0) + return &s_users[i * UUID_SIZE]; + } + + return NULL; +} + /* * Remove an external journal from the filesystem */ -static void remove_journal_device(ext2_filsys fs) +static int remove_journal_device(ext2_filsys fs) { char *journal_path; ext2_filsys jfs; - char buf[1024]; + char buf[SUPERBLOCK_SIZE]; journal_superblock_t *jsb; int i, nr_users; errcode_t retval; @@ -183,7 +246,7 @@ static void remove_journal_device(ext2_filsys fs) journal_path = ext2fs_find_block_device(fs->super->s_journal_dev); if (!journal_path) - return; + goto no_valid_journal; } #ifdef CONFIG_TESTIO_DEBUG @@ -197,38 +260,23 @@ static void remove_journal_device(ext2_filsys fs) EXT2_FLAG_JOURNAL_DEV_OK, 0, fs->blocksize, io_ptr, &jfs); if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while trying to open external journal")); goto no_valid_journal; } - if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { - fprintf(stderr, _("%s is not a journal device.\n"), - journal_path); - goto no_valid_journal; - } - /* Get the journal superblock */ - if ((retval = io_channel_read_blk64(jfs->io, 1, -1024, buf))) { - com_err(program_name, retval, - _("while reading journal superblock")); + if ((retval = get_journal_sb(jfs, buf))) { + if (retval == EXT2_ET_UNSUPP_FEATURE) + fprintf(stderr, _("%s is not a journal device.\n"), + journal_path); goto no_valid_journal; } jsb = (journal_superblock_t *) buf; - if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || - (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) { - fputs(_("Journal superblock not found!\n"), stderr); - goto no_valid_journal; - } - /* Find the filesystem UUID */ nr_users = ntohl(jsb->s_nr_users); - for (i = 0; i < nr_users; i++) { - if (memcmp(fs->super->s_uuid, - &jsb->s_users[i*16], 16) == 0) - break; - } - if (i >= nr_users) { + + if (!journal_user(fs->super->s_uuid, jsb->s_users, nr_users)) { fputs(_("Filesystem's UUID not found on journal device.\n"), stderr); commit_remove_journal = 1; @@ -236,11 +284,14 @@ static void remove_journal_device(ext2_filsys fs) } nr_users--; for (i = 0; i < nr_users; i++) - memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16); + memcpy(&jsb->s_users[i * 16], &jsb->s_users[(i + 1) * 16], 16); jsb->s_nr_users = htonl(nr_users); /* Write back the journal superblock */ - if ((retval = io_channel_write_blk64(jfs->io, 1, -1024, buf))) { + retval = io_channel_write_blk64(jfs->io, + ext2fs_journal_sb_start(fs->blocksize), + -SUPERBLOCK_SIZE, buf); + if (retval) { com_err(program_name, retval, "while writing journal superblock."); goto no_valid_journal; @@ -250,14 +301,19 @@ static void remove_journal_device(ext2_filsys fs) no_valid_journal: if (commit_remove_journal == 0) { - fputs(_("Journal NOT removed\n"), stderr); - exit(1); + fputs(_("Cannot locate journal device. It was NOT removed\n" + "Use -f option to remove missing journal device.\n"), + stderr); + return 1; } fs->super->s_journal_dev = 0; + memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks)); uuid_clear(fs->super->s_journal_uuid); ext2fs_mark_super_dirty(fs); fputs(_("Journal removed\n"), stdout); free(journal_path); + + return 0; } /* Helper function for remove_journal_inode */ @@ -282,7 +338,7 @@ static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, /* * Remove the journal inode from the filesystem */ -static void remove_journal_inode(ext2_filsys fs) +static errcode_t remove_journal_inode(ext2_filsys fs) { struct ext2_inode inode; errcode_t retval; @@ -290,24 +346,24 @@ static void remove_journal_inode(ext2_filsys fs) retval = ext2fs_read_inode(fs, ino, &inode); if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while reading journal inode")); - exit(1); + return retval; } if (ino == EXT2_JOURNAL_INO) { retval = ext2fs_read_bitmaps(fs); if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while reading bitmaps")); - exit(1); + return retval; } retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, NULL, release_blocks_proc, NULL); if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while clearing journal inode")); - exit(1); + return retval; } memset(&inode, 0, sizeof(inode)); ext2fs_mark_bb_dirty(fs); @@ -316,27 +372,41 @@ static void remove_journal_inode(ext2_filsys fs) inode.i_flags &= ~EXT2_IMMUTABLE_FL; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("while writing journal inode")); - exit(1); + return retval; } fs->super->s_journal_inum = 0; ext2fs_mark_super_dirty(fs); + + return 0; } /* * Update the default mount options */ -static void update_mntopts(ext2_filsys fs, char *mntopts) +static int update_mntopts(ext2_filsys fs, char *mntopts) { struct ext2_super_block *sb = fs->super; if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) { fprintf(stderr, _("Invalid mount option set: %s\n"), mntopts); - exit(1); + return 1; } ext2fs_mark_super_dirty(fs); + + return 0; +} + +static int check_fsck_needed(ext2_filsys fs) +{ + if (fs->super->s_state & EXT2_VALID_FS) + return 0; + printf("\n%s\n", _(please_fsck)); + if (mount_flags & EXT2_MF_READONLY) + printf("%s", _("(and reboot afterwards!)\n")); + return 1; } static void request_fsck_afterwards(ext2_filsys fs) @@ -348,18 +418,19 @@ static void request_fsck_afterwards(ext2_filsys fs) fs->super->s_state &= ~EXT2_VALID_FS; printf("\n%s\n", _(please_fsck)); if (mount_flags & EXT2_MF_READONLY) - printf(_("(and reboot afterwards!)\n")); + printf("%s", _("(and reboot afterwards!)\n")); } /* * Update the feature set as provided by the user. */ -static void update_feature_set(ext2_filsys fs, char *features) +static int update_feature_set(ext2_filsys fs, char *features) { struct ext2_super_block *sb = fs->super; struct ext2_group_desc *gd; __u32 old_features[3]; - int i, type_err; + dgrp_t i; + int type_err; unsigned int mask_err; #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \ @@ -390,7 +461,7 @@ static void update_feature_set(ext2_filsys fs, char *features) fprintf(stderr, _("Setting filesystem feature '%s' " "not supported.\n"), e2p_feature2string(type_err, mask_err)); - exit(1); + return 1; } if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { @@ -400,23 +471,107 @@ static void update_feature_set(ext2_filsys fs, char *features) "cleared when the filesystem is\n" "unmounted or mounted " "read-only.\n"), stderr); - exit(1); + return 1; } - if (sb->s_feature_incompat & - EXT3_FEATURE_INCOMPAT_RECOVER) { + if ((sb->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER) && + f_flag < 2) { fputs(_("The needs_recovery flag is set. " "Please run e2fsck before clearing\n" "the has_journal flag.\n"), stderr); - exit(1); + return 1; } if (sb->s_journal_inum) { - remove_journal_inode(fs); + if (remove_journal_inode(fs)) + return 1; } if (sb->s_journal_dev) { - remove_journal_device(fs); + if (remove_journal_device(fs)) + return 1; + } + } + + if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + if (sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_META_BG) { + fputs(_("Setting filesystem feature 'sparse_super' " + "not supported\nfor filesystems with " + "the meta_bg feature enabled.\n"), + stderr); + return 1; + } + } + + if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) { + int error; + + if ((mount_flags & EXT2_MF_MOUNTED) || + (mount_flags & EXT2_MF_READONLY)) { + fputs(_("The multiple mount protection feature can't\n" + "be set if the filesystem is mounted or\n" + "read-only.\n"), stderr); + return 1; + } + + error = ext2fs_mmp_init(fs); + if (error) { + fputs(_("\nError while enabling multiple mount " + "protection feature."), stderr); + return 1; } + + /* + * We want to update group desc with the new free blocks count + */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + + printf(_("Multiple mount protection has been enabled " + "with update interval %ds.\n"), + sb->s_mmp_update_interval); + } + + if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) { + int error; + + if (mount_flags & EXT2_MF_READONLY) { + fputs(_("The multiple mount protection feature cannot\n" + "be disabled if the filesystem is readonly.\n"), + stderr); + return 1; + } + + error = ext2fs_read_bitmaps(fs); + if (error) { + fputs(_("Error while reading bitmaps\n"), stderr); + return 1; + } + + error = ext2fs_mmp_read(fs, sb->s_mmp_block, NULL); + if (error) { + struct mmp_struct *mmp_cmp = fs->mmp_cmp; + + if (error == EXT2_ET_MMP_MAGIC_INVALID) + printf(_("Magic number in MMP block does not " + "match. expected: %x, actual: %x\n"), + EXT4_MMP_MAGIC, mmp_cmp->mmp_magic); + else + com_err(program_name, error, "%s", + _("while reading MMP block.")); + goto mmp_error; + } + + /* We need to force out the group descriptors as well */ + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + ext2fs_block_alloc_stats2(fs, sb->s_mmp_block, -1); +mmp_error: + sb->s_mmp_block = 0; + sb->s_mmp_update_interval = 0; } + if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE)) + sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_EA_INODE; + if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { /* * If adding a journal flag, let the create journal @@ -440,7 +595,7 @@ static void update_feature_set(ext2_filsys fs, char *features) fputs(_("Clearing the flex_bg flag would " "cause the the filesystem to be\n" "inconsistent.\n"), stderr); - exit(1); + return 1; } } @@ -452,7 +607,7 @@ static void update_feature_set(ext2_filsys fs, char *features) "cleared when the filesystem is\n" "unmounted or mounted " "read-only.\n"), stderr); - exit(1); + return 1; } } @@ -537,12 +692,14 @@ static void update_feature_set(ext2_filsys fs, char *features) (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) || (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat)) ext2fs_mark_super_dirty(fs); + + return 0; } /* * Add a journal to the filesystem. */ -static void add_journal(ext2_filsys fs) +static int add_journal(ext2_filsys fs) { unsigned long journal_blocks; errcode_t retval; @@ -555,7 +712,9 @@ static void add_journal(ext2_filsys fs) goto err; } if (journal_device) { - check_plausibility(journal_device); + if (!check_plausibility(journal_device, CHECK_BLOCK_DEV, + NULL)) + proceed_question(-1); check_mount(journal_device, 0, _("journal")); #ifdef CONFIG_TESTIO_DEBUG if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { @@ -578,7 +737,7 @@ static void add_journal(ext2_filsys fs) fflush(stdout); retval = ext2fs_add_journal_device(fs, jfs); - ext2fs_close(jfs); + ext2fs_close_free(&jfs); if (retval) { com_err(program_name, retval, _("while adding filesystem to journal on %s"), @@ -591,13 +750,18 @@ static void add_journal(ext2_filsys fs) fflush(stdout); journal_blocks = figure_journal_size(journal_size, fs); - retval = ext2fs_add_journal_inode(fs, journal_blocks, - journal_flags); + if (journal_location_string) + journal_location = + parse_num_blocks2(journal_location_string, + fs->super->s_log_block_size); + retval = ext2fs_add_journal_inode2(fs, journal_blocks, + journal_location, + journal_flags); if (retval) { fprintf(stderr, "\n"); - com_err(program_name, retval, + com_err(program_name, retval, "%s", _("\n\twhile trying to create journal file")); - exit(1); + return retval; } else fputs(_("done\n"), stdout); /* @@ -609,49 +773,52 @@ static void add_journal(ext2_filsys fs) } print_check_message(fs->super->s_max_mnt_count, fs->super->s_checkinterval); - return; + return 0; err: free(journal_device); - exit(1); + return 1; } -void handle_quota_options(ext2_filsys fs) +static void handle_quota_options(ext2_filsys fs) { quota_ctx_t qctx; - errcode_t retval; ext2_ino_t qf_ino; if (!usrquota && !grpquota) /* Nothing to do. */ return; - init_quota_context(&qctx, fs, -1); + quota_init_context(&qctx, fs, -1); + + if (usrquota == QOPT_ENABLE || grpquota == QOPT_ENABLE) + quota_compute_usage(qctx); if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) { - if ((qf_ino = quota_file_exists(fs, USRQUOTA, QFMT_VFS_V1)) > 0) - set_sb_quota_inum(fs, qf_ino, USRQUOTA); - else - write_quota_inode(qctx, USRQUOTA); + if ((qf_ino = quota_file_exists(fs, USRQUOTA, + QFMT_VFS_V1)) > 0) + quota_update_limits(qctx, qf_ino, USRQUOTA); + quota_write_inode(qctx, USRQUOTA); } else if (usrquota == QOPT_DISABLE) { - remove_quota_inode(fs, USRQUOTA); + quota_remove_inode(fs, USRQUOTA); } if (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) { - if ((qf_ino = quota_file_exists(fs, GRPQUOTA, QFMT_VFS_V1)) > 0) - set_sb_quota_inum(fs, qf_ino, GRPQUOTA); - else - write_quota_inode(qctx, GRPQUOTA); + if ((qf_ino = quota_file_exists(fs, GRPQUOTA, + QFMT_VFS_V1)) > 0) + quota_update_limits(qctx, qf_ino, GRPQUOTA); + quota_write_inode(qctx, GRPQUOTA); } else if (grpquota == QOPT_DISABLE) { - remove_quota_inode(fs, GRPQUOTA); + quota_remove_inode(fs, GRPQUOTA); } - release_quota_context(&qctx); + quota_release_context(&qctx); if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) { fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA; ext2fs_mark_super_dirty(fs); - } else if ((usrquota == QOPT_DISABLE) && (grpquota == QOPT_DISABLE)) { + } else if (!fs->super->s_usr_quota_inum && + !fs->super->s_grp_quota_inum) { fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA; ext2fs_mark_super_dirty(fs); } @@ -659,9 +826,10 @@ void handle_quota_options(ext2_filsys fs) return; } -void parse_quota_opts(const char *opts) +#ifdef CONFIG_QUOTA +static void parse_quota_opts(const char *opts) { - char *buf, *token, *next, *p, *arg; + char *buf, *token, *next, *p; int len; len = strlen(opts); @@ -701,8 +869,7 @@ void parse_quota_opts(const char *opts) } free(buf); } - - +#endif static void parse_e2label_options(int argc, char ** argv) { @@ -764,11 +931,15 @@ static void parse_tune2fs_options(int argc, char **argv) char *tmp; struct group *gr; struct passwd *pw; + char optstring[100] = "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:"; +#ifdef CONFIG_QUOTA + strcat(optstring, "Q:"); +#endif open_flag = 0; printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); - while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:Q:T:U:")) != EOF) + while ((c = getopt(argc, argv, optstring)) != EOF) switch (c) { case 'c': max_mount_count = strtol(optarg, &tmp, 0); @@ -815,7 +986,7 @@ static void parse_tune2fs_options(int argc, char **argv) open_flag |= EXT2_FLAG_RW; break; case 'f': /* Force */ - f_flag = 1; + f_flag++; break; case 'g': resgid = strtoul(optarg, &tmp, 0); @@ -906,28 +1077,29 @@ static void parse_tune2fs_options(int argc, char **argv) break; case 'o': if (mntopts_cmd) { - com_err(program_name, 0, + com_err(program_name, 0, "%s", _("-o may only be specified once")); usage(); } mntopts_cmd = optarg; open_flag = EXT2_FLAG_RW; break; - case 'O': if (features_cmd) { - com_err(program_name, 0, + com_err(program_name, 0, "%s", _("-O may only be specified once")); usage(); } features_cmd = optarg; open_flag = EXT2_FLAG_RW; break; +#ifdef CONFIG_QUOTA case 'Q': Q_flag = 1; parse_quota_opts(optarg); open_flag = EXT2_FLAG_RW; break; +#endif case 'r': reserved_blocks = strtoul(optarg, &tmp, 0); if (*tmp) { @@ -1005,7 +1177,7 @@ static void parse_tune2fs_options(int argc, char **argv) *io_options++ = 0; device_name = blkid_get_devname(NULL, argv[optind], NULL); if (!device_name) { - com_err("tune2fs", 0, _("Unable to resolve '%s'"), + com_err(program_name, 0, _("Unable to resolve '%s'"), argv[optind]); exit(1); } @@ -1032,7 +1204,7 @@ void do_findfs(int argc, char **argv) } #endif -static void parse_extended_opts(ext2_filsys fs, const char *opts) +static int parse_extended_opts(ext2_filsys fs, const char *opts) { char *buf, *token, *next, *p, *arg; int len, hash_alg; @@ -1041,9 +1213,9 @@ static void parse_extended_opts(ext2_filsys fs, const char *opts) len = strlen(opts); buf = malloc(len+1); if (!buf) { - fprintf(stderr, + fprintf(stderr, "%s", _("Couldn't allocate memory to parse options!\n")); - exit(1); + return 1; } strcpy(buf, opts); for (token = buf; token && *token; token = next) { @@ -1058,7 +1230,40 @@ static void parse_extended_opts(ext2_filsys fs, const char *opts) *arg = 0; arg++; } - if (!strcmp(token, "test_fs")) { + if (strcmp(token, "clear-mmp") == 0 || + strcmp(token, "clear_mmp") == 0) { + clear_mmp = 1; + } else if (strcmp(token, "mmp_update_interval") == 0) { + unsigned long intv; + if (!arg) { + r_usage++; + continue; + } + intv = strtoul(arg, &p, 0); + if (*p) { + fprintf(stderr, + _("Invalid mmp_update_interval: %s\n"), + arg); + r_usage++; + continue; + } + if (intv == 0) { + intv = EXT4_MMP_UPDATE_INTERVAL; + } else if (intv > EXT4_MMP_MAX_UPDATE_INTERVAL) { + fprintf(stderr, + _("mmp_update_interval too big: %lu\n"), + intv); + r_usage++; + continue; + } + printf(P_("Setting multiple mount protection update " + "interval to %lu second\n", + "Setting multiple mount protection update " + "interval to %lu seconds\n", intv), + intv); + fs->super->s_mmp_update_interval = intv; + ext2fs_mark_super_dirty(fs); + } else if (!strcmp(token, "test_fs")) { fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS; printf("Setting test filesystem flag\n"); ext2fs_mark_super_dirty(fs); @@ -1074,7 +1279,7 @@ static void parse_extended_opts(ext2_filsys fs, const char *opts) stride = strtoul(arg, &p, 0); if (*p) { fprintf(stderr, - _("Invalid RAID stride: %s\n"), + _("Invalid RAID stride: %s\n"), arg); r_usage++; continue; @@ -1129,11 +1334,12 @@ static void parse_extended_opts(ext2_filsys fs, const char *opts) r_usage++; } if (r_usage) { - fprintf(stderr, _("\nBad options specified.\n\n" + fprintf(stderr, "%s", _("\nBad options specified.\n\n" "Extended options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid extended options are:\n" + "\tclear_mmp\n" "\thash_alg=\n" "\tmount_opts=\n" "\tstride=\n" @@ -1141,9 +1347,11 @@ static void parse_extended_opts(ext2_filsys fs, const char *opts) "\ttest_fs\n" "\t^test_fs\n")); free(buf); - exit(1); + return 1; } free(buf); + + return 0; } /* @@ -1199,10 +1407,10 @@ static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp, return 0; } -static int ext2fs_is_meta_block(ext2_filsys fs, blk_t blk) +static int ext2fs_is_meta_block(ext2_filsys fs, blk64_t blk) { dgrp_t group; - group = ext2fs_group_of_blk(fs, blk); + group = ext2fs_group_of_blk2(fs, blk); if (ext2fs_block_bitmap_loc(fs, group) == blk) return 1; if (ext2fs_inode_bitmap_loc(fs, group) == blk) @@ -1210,11 +1418,11 @@ static int ext2fs_is_meta_block(ext2_filsys fs, blk_t blk) return 0; } -static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk_t blk) +static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk64_t blk) { - blk_t start_blk, end_blk; + blk64_t start_blk, end_blk; start_blk = fs->super->s_first_data_block + - EXT2_BLOCKS_PER_GROUP(fs->super) * group; + EXT2_GROUPS_TO_BLOCKS(fs->super, group); /* * We cannot get new block beyond end_blk for for the last block group * so we can check with EXT2_BLOCKS_PER_GROUP even for last block group @@ -1252,7 +1460,7 @@ static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) * the respective fs metadata pointers. Otherwise * fail */ - group = ext2fs_group_of_blk(fs, blk); + group = ext2fs_group_of_blk2(fs, blk); goal = ext2fs_group_first_block2(fs, group); meta_data = 1; @@ -1370,14 +1578,15 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) * Do we need to fix this ?? */ - if (ext2fs_file_acl_block(&inode) && + if (ext2fs_file_acl_block(fs, &inode) && ext2fs_test_block_bitmap2(bmap, - ext2fs_file_acl_block(&inode))) { - blk = translate_block(ext2fs_file_acl_block(&inode)); + ext2fs_file_acl_block(fs, &inode))) { + blk = translate_block(ext2fs_file_acl_block(fs, + &inode)); if (!blk) continue; - ext2fs_file_acl_block_set(&inode, blk); + ext2fs_file_acl_block_set(fs, &inode, blk); /* * Write the inode to disk so that inode table @@ -1388,7 +1597,7 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) goto err_out; } - if (!ext2fs_inode_has_valid_blocks(&inode)) + if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) continue; retval = ext2fs_block_iterate3(fs, ino, 0, block_buf, @@ -1400,6 +1609,7 @@ static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) err_out: ext2fs_free_mem(&block_buf); + ext2fs_close_inode_scan(scan); return retval; } @@ -1412,7 +1622,7 @@ err_out: static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) { dgrp_t i; - blk_t blk, new_blk; + blk64_t blk, new_blk; for (i = 0; i < fs->group_desc_count; i++) { blk = ext2fs_block_bitmap_loc(fs, i); @@ -1596,7 +1806,7 @@ static int resize_inode(ext2_filsys fs, unsigned long new_size) } retval = ext2fs_read_block_bitmap(fs); if (retval) { - fputs(_("Failed to read blockbitmap\n"), stderr); + fputs(_("Failed to read block bitmap\n"), stderr); return retval; } INIT_LIST_HEAD(&blk_move_list); @@ -1685,7 +1895,7 @@ static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr) tmp_name = strdup(name); if (!tmp_name) { alloc_fn_fail: - com_err(program_name, ENOMEM, + com_err(program_name, ENOMEM, "%s", _("Couldn't allocate memory for tdb filename\n")); return ENOMEM; } @@ -1704,15 +1914,12 @@ static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr) goto alloc_fn_fail; sprintf(tdb_file, "%s/tune2fs-%s.e2undo", tdb_dir, dev_name); - if (!access(tdb_file, F_OK)) { - if (unlink(tdb_file) < 0) { - retval = errno; - com_err(program_name, retval, - _("while trying to delete %s"), - tdb_file); - free(tdb_file); - return retval; - } + if ((unlink(tdb_file) < 0) && (errno != ENOENT)) { + retval = errno; + com_err(program_name, retval, + _("while trying to delete %s"), tdb_file); + free(tdb_file); + return retval; } set_undo_io_backing_manager(*io_ptr); @@ -1726,18 +1933,85 @@ static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr) return retval; } +int +fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE]) +{ + int retval, nr_users, start; + journal_superblock_t *jsb; + ext2_filsys jfs; + __u8 *j_uuid; + char *journal_path; + char uuid[UUID_STR_SIZE]; + char buf[SUPERBLOCK_SIZE]; + + if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) || + uuid_is_null(sb->s_journal_uuid)) + return 0; + + uuid_unparse(sb->s_journal_uuid, uuid); + journal_path = blkid_get_devname(NULL, "UUID", uuid); + if (!journal_path) + return 0; + + retval = ext2fs_open2(journal_path, io_options, + EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_RW, + 0, 0, unix_io_manager, &jfs); + if (retval) { + com_err(program_name, retval, + _("while trying to open %s"), + journal_path); + return retval; + } + + retval = get_journal_sb(jfs, buf); + if (retval != 0) { + if (retval == EXT2_ET_UNSUPP_FEATURE) + fprintf(stderr, _("%s is not a journal device.\n"), + journal_path); + return retval; + } + + jsb = (journal_superblock_t *) buf; + /* Find the filesystem UUID */ + nr_users = ntohl(jsb->s_nr_users); + + j_uuid = journal_user(old_uuid, jsb->s_users, nr_users); + if (j_uuid == NULL) { + fputs(_("Filesystem's UUID not found on journal device.\n"), + stderr); + return EXT2_ET_LOAD_EXT_JOURNAL; + } + + memcpy(j_uuid, sb->s_uuid, UUID_SIZE); + + start = ext2fs_journal_sb_start(jfs->blocksize); + /* Write back the journal superblock */ + retval = io_channel_write_blk64(jfs->io, start, -SUPERBLOCK_SIZE, buf); + if (retval != 0) { + com_err(program_name, retval, + "while writing journal superblock."); + return retval; + } + + ext2fs_close(jfs); + + return 0; +} + int main(int argc, char **argv) { errcode_t retval; ext2_filsys fs; struct ext2_super_block *sb; io_manager io_ptr, io_ptr_orig = NULL; + int rc = 0; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); + set_com_err_gettext(gettext); #endif if (argc && *argv) program_name = *argv; @@ -1761,16 +2035,40 @@ int main(int argc, char **argv) io_ptr = unix_io_manager; retry_open: + if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag) + open_flag |= EXT2_FLAG_SKIP_MMP; + + open_flag |= EXT2_FLAG_64BITS; + + /* keep the filesystem struct around to dump MMP data */ + open_flag |= EXT2_FLAG_NOFREE_ON_ERROR; + retval = ext2fs_open2(device_name, io_options, open_flag, 0, 0, io_ptr, &fs); if (retval) { - com_err(program_name, retval, - _("while trying to open %s"), + com_err(program_name, retval, + _("while trying to open %s"), device_name); - fprintf(stderr, - _("Couldn't find valid filesystem superblock.\n")); + if (retval == EXT2_ET_MMP_FSCK_ON || + retval == EXT2_ET_MMP_UNKNOWN_SEQ) + dump_mmp_msg(fs->mmp_buf, + _("If you are sure the filesystem " + "is not in use on any node, run:\n" + "'tune2fs -f -E clear_mmp {device}'\n")); + else if (retval == EXT2_ET_MMP_FAILED) + dump_mmp_msg(fs->mmp_buf, NULL); + else if (retval == EXT2_ET_MMP_MAGIC_INVALID) + fprintf(stderr, + _("MMP block magic is bad. Try to fix it by " + "running:\n'e2fsck -f %s'\n"), device_name); + else if (retval != EXT2_ET_MMP_FAILED) + fprintf(stderr, "%s", + _("Couldn't find valid filesystem superblock.\n")); + + ext2fs_free(fs); exit(1); } + fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; if (I_flag && !io_ptr_orig) { /* @@ -1781,12 +2079,20 @@ retry_open: if (new_inode_size == EXT2_INODE_SIZE(fs->super)) { fprintf(stderr, _("The inode size is already %lu\n"), new_inode_size); - exit(1); + rc = 1; + goto closefs; } if (new_inode_size < EXT2_INODE_SIZE(fs->super)) { - fprintf(stderr, _("Shrinking the inode size is " - "not supported\n")); - exit(1); + fprintf(stderr, "%s", + _("Shrinking inode size is not supported\n")); + rc = 1; + goto closefs; + } + if (new_inode_size > fs->blocksize) { + fprintf(stderr, _("Invalid inode size %lu (max %d)\n"), + new_inode_size, fs->blocksize); + rc = 1; + goto closefs; } /* @@ -1795,10 +2101,12 @@ retry_open: */ io_ptr_orig = io_ptr; retval = tune2fs_setup_tdb(device_name, &io_ptr); - if (retval) - exit(1); + if (retval) { + rc = 1; + goto closefs; + } if (io_ptr != io_ptr_orig) { - ext2fs_close(fs); + ext2fs_close_free(&fs); goto retry_open; } } @@ -1811,7 +2119,7 @@ retry_open: printf("%.*s\n", (int) sizeof(sb->s_volume_name), sb->s_volume_name); remove_error_table(&et_ext2_error_table); - exit(0); + goto closefs; } retval = ext2fs_check_if_mounted(device_name, &mount_flags); @@ -1819,7 +2127,8 @@ retry_open: com_err("ext2fs_check_if_mount", retval, _("while determining whether %s is mounted."), device_name); - exit(1); + rc = 1; + goto closefs; } /* Normally we only need to write out the superblock */ fs->flags |= EXT2_FLAG_SUPER_ONLY; @@ -1846,11 +2155,12 @@ retry_open: printf(_("Setting reserved blocks gid to %lu\n"), resgid); } if (i_flag) { - if (interval >= (1ULL << 32)) { + if ((unsigned long long)interval >= (1ULL << 32)) { com_err(program_name, 0, _("interval between checks is too big (%lu)"), interval); - exit(1); + rc = 1; + goto closefs; } sb->s_checkinterval = interval; ext2fs_mark_super_dirty(fs); @@ -1869,7 +2179,8 @@ retry_open: com_err(program_name, 0, _("reserved blocks count is too big (%llu)"), reserved_blocks); - exit(1); + rc = 1; + goto closefs; } ext2fs_r_blocks_count_set(sb, reserved_blocks); ext2fs_mark_super_dirty(fs); @@ -1878,10 +2189,18 @@ retry_open: } if (s_flag == 1) { if (sb->s_feature_ro_compat & - EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { fputs(_("\nThe filesystem already has sparse " "superblocks.\n"), stderr); - else { + } else if (sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_META_BG) { + fputs(_("\nSetting the sparse superblock flag not " + "supported\nfor filesystems with " + "the meta_bg feature enabled.\n"), + stderr); + rc = 1; + goto closefs; + } else { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; sb->s_state &= ~EXT2_VALID_FS; @@ -1891,9 +2210,10 @@ retry_open: } } if (s_flag == 0) { - fputs(_("\nClearing the sparse superflag not supported.\n"), + fputs(_("\nClearing the sparse superblock flag not supported.\n"), stderr); - exit(1); + rc = 1; + goto closefs; } if (T_flag) { sb->s_lastcheck = last_check_time; @@ -1921,20 +2241,43 @@ retry_open: sizeof(sb->s_last_mounted)); ext2fs_mark_super_dirty(fs); } - if (mntopts_cmd) - update_mntopts(fs, mntopts_cmd); - if (features_cmd) - update_feature_set(fs, features_cmd); - if (extended_cmd) - parse_extended_opts(fs, extended_cmd); - if (journal_size || journal_device) - add_journal(fs); + if (mntopts_cmd) { + rc = update_mntopts(fs, mntopts_cmd); + if (rc) + goto closefs; + } + if (features_cmd) { + rc = update_feature_set(fs, features_cmd); + if (rc) + goto closefs; + } + if (extended_cmd) { + rc = parse_extended_opts(fs, extended_cmd); + if (rc) + goto closefs; + if (clear_mmp && !f_flag) { + fputs(_("Error in using clear_mmp. " + "It must be used with -f\n"), + stderr); + goto closefs; + } + } + if (clear_mmp) { + rc = ext2fs_mmp_clear(fs); + goto closefs; + } + if (journal_size || journal_device) { + rc = add_journal(fs); + if (rc) + goto closefs; + } if (Q_flag) { if (mount_flags & EXT2_MF_MOUNTED) { fputs(_("The quota feature may only be changed when " "the filesystem is unmounted.\n"), stderr); - exit(1); + rc = 1; + goto closefs; } handle_quota_options(fs); } @@ -1942,10 +2285,25 @@ retry_open: if (U_flag) { int set_csum = 0; dgrp_t i; + char buf[SUPERBLOCK_SIZE]; + __u8 old_uuid[UUID_SIZE]; if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { /* + * Changing the UUID requires rewriting all metadata, + * which can race with a mounted fs. Don't allow that. + */ + if (mount_flags & EXT2_MF_MOUNTED) { + fputs(_("The UUID may only be " + "changed when the filesystem is " + "unmounted.\n"), stderr); + exit(1); + } + if (check_fsck_needed(fs)) + exit(1); + + /* * Determine if the block group checksums are * correct so we know whether or not to set * them later on. @@ -1956,6 +2314,8 @@ retry_open: if (i >= fs->group_desc_count) set_csum = 1; } + + memcpy(old_uuid, sb->s_uuid, UUID_SIZE); if ((strcasecmp(new_UUID, "null") == 0) || (strcasecmp(new_UUID, "clear") == 0)) { uuid_clear(sb->s_uuid); @@ -1964,14 +2324,39 @@ retry_open: } else if (strcasecmp(new_UUID, "random") == 0) { uuid_generate(sb->s_uuid); } else if (uuid_parse(new_UUID, sb->s_uuid)) { - com_err(program_name, 0, _("Invalid UUID format\n")); - exit(1); + com_err(program_name, 0, "%s", + _("Invalid UUID format\n")); + rc = 1; + goto closefs; } if (set_csum) { for (i = 0; i < fs->group_desc_count; i++) ext2fs_group_desc_csum_set(fs, i); fs->flags &= ~EXT2_FLAG_SUPER_ONLY; } + + /* If this is a journal dev, we need to copy UUID into jsb */ + if (!(rc = get_journal_sb(fs, buf))) { + journal_superblock_t *jsb; + + jsb = (journal_superblock_t *) buf; + fputs(_("Need to update journal superblock.\n"), stdout); + memcpy(jsb->s_uuid, sb->s_uuid, sizeof(sb->s_uuid)); + + /* Writeback the journal superblock */ + if ((rc = io_channel_write_blk64(fs->io, + ext2fs_journal_sb_start(fs->blocksize), + -SUPERBLOCK_SIZE, buf))) + goto closefs; + } else if (rc != EXT2_ET_UNSUPP_FEATURE) + goto closefs; + else { + rc = 0; /** Reset rc to avoid ext2fs_mmp_stop() */ + + if ((rc = fs_update_journal_user(sb, old_uuid))) + goto closefs; + } + ext2fs_mark_super_dirty(fs); } if (I_flag) { @@ -1979,7 +2364,8 @@ retry_open: fputs(_("The inode size may only be " "changed when the filesystem is " "unmounted.\n"), stderr); - exit(1); + rc = 1; + goto closefs; } if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) { @@ -1987,7 +2373,8 @@ retry_open: "filesystems with the flex_bg\n" "feature enabled.\n"), stderr); - exit(1); + rc = 1; + goto closefs; } /* * We want to update group descriptor also @@ -1998,8 +2385,9 @@ retry_open: printf(_("Setting inode size %lu\n"), new_inode_size); } else { - printf(_("Failed to change inode size\n")); - exit(1); + printf("%s", _("Failed to change inode size\n")); + rc = 1; + goto closefs; } } @@ -2015,8 +2403,9 @@ retry_open: ext2fs_mark_super_dirty(fs); printf(_("Setting stripe width to %d\n"), stripe_width); } + if (ext_mount_opts) { - strncpy(fs->super->s_mount_opts, ext_mount_opts, + strncpy((char *)(fs->super->s_mount_opts), ext_mount_opts, sizeof(fs->super->s_mount_opts)); fs->super->s_mount_opts[sizeof(fs->super->s_mount_opts)-1] = 0; ext2fs_mark_super_dirty(fs); @@ -2024,7 +2413,25 @@ retry_open: ext_mount_opts); free(ext_mount_opts); } + + /* Warn if file system needs recovery and it is opened for writing. */ + if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) && + (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) { + fprintf(stderr, + _("Warning: needs_recovery flag is set. You may wish\n" + "replay the journal then rerun this command, or any\n" + "changes may be overwritten by journal recovery.\n")); + } + free(device_name); remove_error_table(&et_ext2_error_table); - return (ext2fs_close(fs) ? 1 : 0); + +closefs: + if (rc) { + ext2fs_mmp_stop(fs); + exit(1); + } + + return (ext2fs_close_free(&fs) ? 1 : 0); }