X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=misc%2Ftune2fs.c;h=818c8a1fd575bc4876ec6bc700f2e51152bc1ad0;hb=1be672b7d5c09b74ff72735e6b518bb3ec364000;hp=66db3a4e52e326ae9c720451b90af056360cc939;hpb=896938d57e7091e7a032674dfeeb91f2a17fd78b;p=tools%2Fe2fsprogs.git diff --git a/misc/tune2fs.c b/misc/tune2fs.c index 66db3a4..818c8a1 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -1,12 +1,11 @@ /* - * tune2fs.c - Change the file system parameters on - * an unmounted second extended file system + * tune2fs.c - Change the file system parameters on an ext2 file system * * Copyright (C) 1992, 1993, 1994 Remy Card * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * - * Copyright 1995, 1996, 1997 by Theodore Ts'o. + * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public @@ -30,6 +29,9 @@ #include #ifdef HAVE_GETOPT_H #include +#else +extern char *optarg; +extern int optind; #endif #include #include @@ -39,105 +41,439 @@ #include #include -#include - +#include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" #include "et/com_err.h" #include "uuid/uuid.h" #include "e2p/e2p.h" +#include "jfs_user.h" +#include "util.h" +#include "blkid/blkid.h" #include "../version.h" +#include "nls-enable.h" const char * program_name = "tune2fs"; -char * device_name = NULL; -char * new_label = NULL; -char * new_last_mounted = NULL; -char * new_UUID = NULL; -int c_flag = 0; -int C_flag = 0; -int e_flag = 0; -int g_flag = 0; -int i_flag = 0; -int l_flag = 0; -int L_flag = 0; -int m_flag = 0; -int M_flag = 0; -int r_flag = 0; -int s_flag = -1; -int u_flag = 0; -int U_flag = 0; -int max_mount_count, mount_count; -unsigned long interval; -unsigned long reserved_ratio = 0; -unsigned long reserved_blocks = 0; -unsigned short errors; -unsigned long resgid = 0; -unsigned long resuid = 0; - -#ifndef HAVE_STRCASECMP -static int strcasecmp (char *s1, char *s2) -{ - while (*s1 && *s2) { - int ch1 = *s1++, ch2 = *s2++; - if (isupper (ch1)) - ch1 = tolower (ch1); - if (isupper (ch2)) - ch2 = tolower (ch2); - if (ch1 != ch2) - return ch1 - ch2; - } - return *s1 ? 1 : *s2 ? -1 : 0; -} -#endif +char * device_name; +char * new_label, *new_last_mounted, *new_UUID; +static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; +static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; +static time_t last_check_time; +static int print_label; +static int max_mount_count, mount_count, mount_flags; +static unsigned long interval, reserved_ratio, reserved_blocks; +static unsigned long resgid, resuid; +static unsigned short errors; +static int open_flag; +static char *features_cmd; +static char *mntopts_cmd; + +int journal_size, journal_flags; +char *journal_device; + +static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n"); + +void do_findfs(int argc, char **argv); static void usage(void) { - fprintf(stderr, "Usage: %s [-c max-mounts-count] [-e errors-behavior] " - "[-g group]\n" - "\t[-i interval[d|m|w]] [-l] [-s] [-m reserved-blocks-percent]\n" - "\t[-r reserved-blocks-count] [-u user] [-C mount-count]\n" - "\t[-L volume-label] [-M last-mounted-dir] [-U UUID]\n" - "\t[-O [-]feature[,...]] device\n", program_name); + fprintf(stderr, + _("Usage: %s [-c max-mounts-count] [-e errors-behavior] " + "[-g group]\n" + "\t[-i interval[d|m|w]] [-j] [-J journal-options]\n" + "\t[-l] [-s sparse-flag] [-m reserved-blocks-percent]\n" + "\t[-o [^]mount-options[,...]] [-r reserved-blocks-count]\n" + "\t[-u user] [-C mount-count] [-L volume-label] " + "[-M last-mounted-dir]\n" + "\t[-O [^]feature[,...]] [-T last-check-time] [-U UUID]" + " device\n"), program_name); exit (1); } static __u32 ok_features[3] = { - 0, /* Compat */ + EXT3_FEATURE_COMPAT_HAS_JOURNAL | + EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */ EXT2_FEATURE_INCOMPAT_FILETYPE, /* Incompat */ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */ }; -static const char *please_fsck = "Please run e2fsck on the filesystem.\n"; +/* + * Remove an external journal from the filesystem + */ +static void remove_journal_device(ext2_filsys fs) +{ + char *journal_path; + ext2_filsys jfs; + char buf[1024]; + journal_superblock_t *jsb; + int i, nr_users; + errcode_t retval; + int commit_remove_journal = 0; -int main (int argc, char ** argv) + if (f_flag) + commit_remove_journal = 1; /* force removal even if error */ + + uuid_unparse(fs->super->s_journal_uuid, buf); + journal_path = blkid_get_devname(NULL, "UUID", buf); + + if (!journal_path) { + journal_path = + ext2fs_find_block_device(fs->super->s_journal_dev); + if (!journal_path) + return; + } + + retval = ext2fs_open(journal_path, EXT2_FLAG_RW| + EXT2_FLAG_JOURNAL_DEV_OK, 0, + fs->blocksize, unix_io_manager, &jfs); + if (retval) { + com_err(program_name, retval, + _("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_blk(jfs->io, 1, -1024, buf))) { + com_err(program_name, retval, + _("while reading journal superblock")); + 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))) { + fprintf(stderr, _("Journal superblock not found!\n")); + 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) { + fprintf(stderr, + _("Filesystem's UUID not found on journal device.\n")); + commit_remove_journal = 1; + goto no_valid_journal; + } + nr_users--; + for (i=0; i < nr_users; i++) + 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_blk(jfs->io, 1, -1024, buf))) { + com_err(program_name, retval, + "while writing journal superblock."); + goto no_valid_journal; + } + + commit_remove_journal = 1; + +no_valid_journal: + if (commit_remove_journal == 0) { + fprintf(stderr, _("Journal NOT removed\n")); + exit(1); + } + fs->super->s_journal_dev = 0; + uuid_clear(fs->super->s_journal_uuid); + ext2fs_mark_super_dirty(fs); + printf(_("Journal removed\n")); + free(journal_path); +} + +/* Helper function for remove_journal_inode */ +static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, + int blockcnt, void *private) +{ + blk_t block; + int group; + + block = *blocknr; + ext2fs_unmark_block_bitmap(fs->block_map,block); + group = ext2fs_group_of_blk(fs, block); + fs->group_desc[group].bg_free_blocks_count++; + fs->super->s_free_blocks_count++; + return 0; +} + +/* + * Remove the journal inode from the filesystem + */ +static void remove_journal_inode(ext2_filsys fs) +{ + struct ext2_inode inode; + errcode_t retval; + ino_t ino = fs->super->s_journal_inum; + + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while reading journal inode")); + exit(1); + } + if (ino == EXT2_JOURNAL_INO) { + retval = ext2fs_read_bitmaps(fs); + if (retval) { + com_err(program_name, retval, + _("while reading bitmaps")); + exit(1); + } + retval = ext2fs_block_iterate(fs, ino, 0, NULL, + release_blocks_proc, NULL); + if (retval) { + com_err(program_name, retval, + _("while clearing journal inode")); + exit(1); + } + memset(&inode, 0, sizeof(inode)); + ext2fs_mark_bb_dirty(fs); + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } else + inode.i_flags &= ~EXT2_IMMUTABLE_FL; + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) { + com_err(program_name, retval, + _("while writing journal inode")); + exit(1); + } + fs->super->s_journal_inum = 0; + ext2fs_mark_super_dirty(fs); +} + +/* + * Update the default mount options + */ +static void 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); + } + ext2fs_mark_super_dirty(fs); +} + +/* + * Update the feature set as provided by the user. + */ +static void update_feature_set(ext2_filsys fs, char *features) +{ + int sparse, old_sparse, filetype, old_filetype; + int journal, old_journal, dxdir, old_dxdir; + struct ext2_super_block *sb= fs->super; + + old_sparse = sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + old_filetype = sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE; + old_journal = sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + old_dxdir = sb->s_feature_compat & + EXT2_FEATURE_COMPAT_DIR_INDEX; + if (e2p_edit_feature(features, &sb->s_feature_compat, + ok_features)) { + fprintf(stderr, _("Invalid filesystem option set: %s\n"), + features); + exit(1); + } + sparse = sb->s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; + filetype = sb->s_feature_incompat & + EXT2_FEATURE_INCOMPAT_FILETYPE; + journal = sb->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL; + dxdir = sb->s_feature_compat & + EXT2_FEATURE_COMPAT_DIR_INDEX; + if (old_journal && !journal) { + if ((mount_flags & EXT2_MF_MOUNTED) && + !(mount_flags & EXT2_MF_READONLY)) { + fprintf(stderr, + _("The has_journal flag may only be " + "cleared when the filesystem is\n" + "unmounted or mounted " + "read-only.\n")); + exit(1); + } + if (sb->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_RECOVER) { + fprintf(stderr, + _("The needs_recovery flag is set. " + "Please run e2fsck before clearing\n" + "the has_journal flag.\n")); + exit(1); + } + if (sb->s_journal_inum) { + remove_journal_inode(fs); + } + if (sb->s_journal_dev) { + remove_journal_device(fs); + } + } + if (journal && !old_journal) { + /* + * If adding a journal flag, let the create journal + * code below handle creating setting the flag and + * creating the journal. We supply a default size if + * necessary. + */ + if (!journal_size) + journal_size = -1; + sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; + } + if (dxdir && !old_dxdir) { + if (!sb->s_def_hash_version) + sb->s_def_hash_version = EXT2_HASH_TEA; + if (uuid_is_null((unsigned char *) sb->s_hash_seed)) + uuid_generate((unsigned char *) sb->s_hash_seed); + } + + if (sb->s_rev_level == EXT2_GOOD_OLD_REV && + (sb->s_feature_compat || sb->s_feature_ro_compat || + sb->s_feature_incompat)) + ext2fs_update_dynamic_rev(fs); + if ((sparse != old_sparse) || + (filetype != old_filetype)) { + sb->s_state &= ~EXT2_VALID_FS; + printf("\n%s\n", _(please_fsck)); + } + ext2fs_mark_super_dirty(fs); +} + +/* + * Add a journal to the filesystem. + */ +static void add_journal(ext2_filsys fs) +{ + unsigned long journal_blocks; + errcode_t retval; + ext2_filsys jfs; + + if (fs->super->s_feature_compat & + EXT3_FEATURE_COMPAT_HAS_JOURNAL) { + fprintf(stderr, _("The filesystem already has a journal.\n")); + goto err; + } + if (journal_device) { + check_plausibility(journal_device); + check_mount(journal_device, 0, _("journal")); + retval = ext2fs_open(journal_device, EXT2_FLAG_RW| + EXT2_FLAG_JOURNAL_DEV_OK, 0, + fs->blocksize, unix_io_manager, &jfs); + if (retval) { + com_err(program_name, retval, + _("\n\twhile trying to open journal on %s\n"), + journal_device); + goto err; + } + printf(_("Creating journal on device %s: "), + journal_device); + fflush(stdout); + + retval = ext2fs_add_journal_device(fs, jfs); + ext2fs_close(jfs); + if (retval) { + com_err (program_name, retval, + _("while adding filesystem to journal on %s"), + journal_device); + goto err; + } + printf(_("done\n")); + } else if (journal_size) { + printf(_("Creating journal inode: ")); + fflush(stdout); + journal_blocks = figure_journal_size(journal_size, fs); + + retval = ext2fs_add_journal_inode(fs, journal_blocks, + journal_flags); + if (retval) { + fprintf(stderr, "\n"); + com_err(program_name, retval, + _("\n\twhile trying to create journal file")); + exit(1); + } else + printf(_("done\n")); + /* + * If the filesystem wasn't mounted, we need to force + * the block group descriptors out. + */ + if ((mount_flags & EXT2_MF_MOUNTED) == 0) + fs->flags &= ~EXT2_FLAG_SUPER_ONLY; + } + print_check_message(fs); + return; + +err: + if (journal_device) + free(journal_device); + exit(1); +} + + +static void parse_e2label_options(int argc, char ** argv) +{ + if ((argc < 2) || (argc > 3)) { + fprintf(stderr, _("Usage: e2label device [newlabel]\n")); + exit(1); + } + device_name = blkid_get_devname(NULL, argv[1], NULL); + if (argc == 3) { + open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK; + L_flag = 1; + new_label = argv[2]; + } else + print_label++; +} + +static time_t parse_time(char *str) +{ + struct tm ts; + + if (strcmp(str, "now") == 0) { + return (time(0)); + } + memset(&ts, 0, sizeof(ts)); + strptime(optarg, "%Y%m%d%H%M%S", &ts); + if (ts.tm_mday == 0) { + com_err(program_name, 0, + _("Couldn't parse date/time specifier: %s"), + str); + usage(); + } + return (mktime(&ts)); +} + +static void parse_tune2fs_options(int argc, char **argv) { int c; char * tmp; - errcode_t retval; - ext2_filsys fs; - struct ext2fs_sb *sb; struct group * gr; struct passwd * pw; - int open_flag = 0; - char *features_cmd = 0; - fprintf (stderr, "tune2fs %s, %s for EXT2 FS %s, %s\n", - E2FSPROGS_VERSION, E2FSPROGS_DATE, - EXT2FS_VERSION, EXT2FS_DATE); - if (argc && *argv) - program_name = *argv; - initialize_ext2_error_table(); - while ((c = getopt (argc, argv, "c:e:g:i:lm:r:s:u:C:L:M:O:U:")) != EOF) + printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); + while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF) switch (c) { case 'c': - max_mount_count = strtoul (optarg, &tmp, 0); + max_mount_count = strtol (optarg, &tmp, 0); if (*tmp || max_mount_count > 16000) { com_err (program_name, 0, - "bad mounts count - %s", + _("bad mounts count - %s"), optarg); usage(); } + if (max_mount_count == 0) + max_mount_count = -1; c_flag = 1; open_flag = EXT2_FLAG_RW; break; @@ -145,7 +481,7 @@ int main (int argc, char ** argv) mount_count = strtoul (optarg, &tmp, 0); if (*tmp || mount_count > 16000) { com_err (program_name, 0, - "bad mounts count - %s", + _("bad mounts count - %s"), optarg); usage(); } @@ -161,13 +497,16 @@ int main (int argc, char ** argv) errors = EXT2_ERRORS_PANIC; else { com_err (program_name, 0, - "bad error behavior - %s", + _("bad error behavior - %s"), optarg); usage(); } e_flag = 1; open_flag = EXT2_FLAG_RW; break; + case 'f': /* Force */ + f_flag = 1; + break; case 'g': resgid = strtoul (optarg, &tmp, 0); if (*tmp) { @@ -181,7 +520,7 @@ int main (int argc, char ** argv) } if (*tmp) { com_err (program_name, 0, - "bad gid/group name - %s", + _("bad gid/group name - %s"), optarg); usage(); } @@ -214,25 +553,35 @@ int main (int argc, char ** argv) } if (*tmp || interval > (365 * 86400)) { com_err (program_name, 0, - "bad interval - %s", optarg); + _("bad interval - %s"), optarg); usage(); } i_flag = 1; open_flag = EXT2_FLAG_RW; break; + case 'j': + if (!journal_size) + journal_size = -1; + open_flag = EXT2_FLAG_RW; + break; + case 'J': + parse_journal_opts(optarg); + open_flag = EXT2_FLAG_RW; + break; case 'l': l_flag = 1; break; case 'L': new_label = optarg; L_flag = 1; - open_flag = EXT2_FLAG_RW; + open_flag = EXT2_FLAG_RW | + EXT2_FLAG_JOURNAL_DEV_OK; break; case 'm': reserved_ratio = strtoul (optarg, &tmp, 0); if (*tmp || reserved_ratio > 50) { com_err (program_name, 0, - "bad reserved block ratio - %s", + _("bad reserved block ratio - %s"), optarg); usage(); } @@ -244,7 +593,22 @@ int main (int argc, char ** argv) M_flag = 1; open_flag = EXT2_FLAG_RW; break; + case 'o': + if (mntopts_cmd) { + com_err (program_name, 0, + _("-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, + _("-O may only be specified once")); + usage(); + } features_cmd = optarg; open_flag = EXT2_FLAG_RW; break; @@ -252,7 +616,7 @@ int main (int argc, char ** argv) reserved_blocks = strtoul (optarg, &tmp, 0); if (*tmp) { com_err (program_name, 0, - "bad reserved blocks count - %s", + _("bad reserved blocks count - %s"), optarg); usage(); } @@ -263,6 +627,11 @@ int main (int argc, char ** argv) s_flag = atoi(optarg); open_flag = EXT2_FLAG_RW; break; + case 'T': + T_flag = 1; + last_check_time = parse_time(optarg); + open_flag = EXT2_FLAG_RW; + break; case 'u': resuid = strtoul (optarg, &tmp, 0); if (*tmp) { @@ -276,7 +645,7 @@ int main (int argc, char ** argv) } if (*tmp) { com_err (program_name, 0, - "bad uid/user name - %s", + _("bad uid/user name - %s"), optarg); usage(); } @@ -286,7 +655,8 @@ int main (int argc, char ** argv) case 'U': new_UUID = optarg; U_flag = 1; - open_flag = EXT2_FLAG_RW; + open_flag = EXT2_FLAG_RW | + EXT2_FLAG_JOURNAL_DEV_OK; break; default: usage(); @@ -295,127 +665,167 @@ int main (int argc, char ** argv) usage(); if (!open_flag && !l_flag) usage(); - device_name = argv[optind]; + device_name = blkid_get_devname(NULL, argv[optind], NULL); +} + +void do_findfs(int argc, char **argv) +{ + char *dev; + + if ((argc != 2) || + (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) { + fprintf(stderr, "Usage: findfs LABEL=