e2fsprogs/misc/tune2fs.c

3230 lines
81 KiB
C
Raw Normal View History

1997-04-26 21:21:57 +08:00
/*
* tune2fs.c - Change the file system parameters on an ext2 file system
1997-04-26 21:21:57 +08:00
*
* Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
* Laboratoire MASI, Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
1997-04-30 00:17:09 +08:00
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
1997-04-26 21:21:57 +08:00
*/
/*
* History:
* 93/06/01 - Creation
* 93/10/31 - Added the -c option to change the maximal mount counts
* 93/12/14 - Added -l flag to list contents of superblock
* M.J.E. Mol (marcel@duteca.et.tudelft.nl)
* F.W. ten Wolde (franky@duteca.et.tudelft.nl)
* 93/12/29 - Added the -e option to change errors behavior
* 94/02/27 - Ported to use the ext2fs library
* 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
*/
#define _XOPEN_SOURCE 600 /* for inclusion of strptime() */
#include "config.h"
1997-04-26 21:21:57 +08:00
#include <fcntl.h>
1997-04-26 21:34:30 +08:00
#include <grp.h>
1997-04-26 22:00:26 +08:00
#ifdef HAVE_GETOPT_H
1997-04-26 21:21:57 +08:00
#include <getopt.h>
#else
extern char *optarg;
extern int optind;
1997-04-26 22:00:26 +08:00
#endif
1997-04-26 21:34:30 +08:00
#include <pwd.h>
1997-04-26 21:21:57 +08:00
#include <stdio.h>
#ifdef HAVE_STDLIB_H
1997-04-26 21:21:57 +08:00
#include <stdlib.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h> /* for strcasecmp() */
#else
#define _BSD_SOURCE /* for inclusion of strcasecmp() via <string.h> */
#define _DEFAULT_SOURCE /* since glibc 2.20 _BSD_SOURCE is deprecated */
#endif
1997-04-26 21:21:57 +08:00
#include <string.h>
#include <time.h>
#include <unistd.h>
1997-04-26 21:34:30 +08:00
#include <sys/types.h>
#include <libgen.h>
#include <limits.h>
1997-04-26 21:21:57 +08:00
#include "ext2fs/ext2_fs.h"
1997-04-26 21:21:57 +08:00
#include "ext2fs/ext2fs.h"
#include "ext2fs/kernel-jbd.h"
1997-04-26 21:21:57 +08:00
#include "et/com_err.h"
#include "support/plausible.h"
#include "support/quotaio.h"
1997-04-29 22:53:37 +08:00
#include "uuid/uuid.h"
1997-04-26 21:21:57 +08:00
#include "e2p/e2p.h"
#include "util.h"
#include "blkid/blkid.h"
1997-04-26 21:21:57 +08:00
#include "../version.h"
#include "support/nls-enable.h"
1997-04-26 21:21:57 +08:00
#define QOPT_ENABLE (1)
#define QOPT_DISABLE (-1)
extern int ask_yn(const char *string, int def);
const char *program_name = "tune2fs";
char *device_name;
char *new_label, *new_last_mounted, *new_UUID;
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;
static unsigned long interval;
static blk64_t reserved_blocks;
static double reserved_ratio;
static unsigned long resgid, resuid;
static unsigned short errors;
static int open_flag;
static char *features_cmd;
static char *mntopts_cmd;
static int stride, stripe_width;
static int stride_set, stripe_width_set;
static char *extended_cmd;
static unsigned long new_inode_size;
static char *ext_mount_opts;
static int quota_enable[MAXQUOTAS];
static int rewrite_checksums;
static int feature_64bit;
static int fsck_requested;
static char *undo_file;
1997-04-26 21:21:57 +08:00
int journal_size, journal_flags;
char *journal_device;
static blk64_t journal_location = ~0LL;
static struct list_head blk_move_list;
struct blk_move {
struct list_head list;
blk64_t old_loc;
blk64_t new_loc;
};
errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
static const char *please_dir_fsck =
N_("Please run e2fsck -D on the filesystem.\n");
1997-04-29 22:53:37 +08:00
#ifdef CONFIG_BUILD_FINDFS
void do_findfs(int argc, char **argv);
#endif
#ifdef CONFIG_JBD_DEBUG /* Enabled by configure --enable-jbd-debug */
int journal_enable_debug = -1;
#endif
static void usage(void)
1997-04-26 21:21:57 +08:00
{
fprintf(stderr,
_("Usage: %s [-c max_mounts_count] [-e errors_behavior] [-f] "
"[-g group]\n"
"\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
"\t[-m reserved_blocks_percent] [-o [^]mount_options[,...]]\n"
"\t[-p mmp_update_interval] [-r reserved_blocks_count] "
"[-u user]\n"
"\t[-C mount_count] [-L volume_label] [-M last_mounted_dir]\n"
"\t[-O [^]feature[,...]] [-Q quota_options]\n"
"\t[-E extended-option[,...]] [-T last_check_time] "
"[-U UUID]\n\t[-I new_inode_size] [-z undo_file] device\n"),
program_name);
exit(1);
1997-04-26 21:21:57 +08:00
}
static __u32 ok_features[3] = {
/* Compat */
EXT3_FEATURE_COMPAT_HAS_JOURNAL |
EXT2_FEATURE_COMPAT_DIR_INDEX,
/* Incompat */
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT3_FEATURE_INCOMPAT_EXTENTS |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
EXT4_FEATURE_INCOMPAT_MMP |
EXT4_FEATURE_INCOMPAT_64BIT |
EXT4_FEATURE_INCOMPAT_ENCRYPT |
EXT4_FEATURE_INCOMPAT_CSUM_SEED,
/* 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 |
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
EXT4_FEATURE_RO_COMPAT_READONLY |
EXT4_FEATURE_RO_COMPAT_PROJECT
};
static __u32 clear_ok_features[3] = {
/* Compat */
EXT3_FEATURE_COMPAT_HAS_JOURNAL |
EXT2_FEATURE_COMPAT_RESIZE_INODE |
EXT2_FEATURE_COMPAT_DIR_INDEX,
/* Incompat */
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
EXT4_FEATURE_INCOMPAT_MMP |
EXT4_FEATURE_INCOMPAT_64BIT |
EXT4_FEATURE_INCOMPAT_CSUM_SEED,
/* 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 |
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
EXT4_FEATURE_RO_COMPAT_READONLY
};
/**
* 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 (!ext2fs_has_feature_journal_dev(jfs->super)) {
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 int remove_journal_device(ext2_filsys fs)
{
char *journal_path;
ext2_filsys jfs;
char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
journal_superblock_t *jsb;
int i, nr_users;
errcode_t retval;
int commit_remove_journal = 0;
io_manager io_ptr;
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)
goto no_valid_journal;
}
#ifdef CONFIG_TESTIO_DEBUG
if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
io_ptr = test_io_manager;
test_io_backing_manager = unix_io_manager;
} else
#endif
io_ptr = unix_io_manager;
retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
EXT2_FLAG_JOURNAL_DEV_OK, 0,
fs->blocksize, io_ptr, &jfs);
if (retval) {
com_err(program_name, retval, "%s",
_("while trying to open external journal"));
goto no_valid_journal;
}
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;
/* Find the filesystem UUID */
nr_users = ntohl(jsb->s_nr_users);
if (!journal_user(fs->super->s_uuid, jsb->s_users, nr_users)) {
fputs(_("Filesystem's UUID not found on journal device.\n"),
2003-12-07 14:28:50 +08:00
stderr);
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 */
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;
}
commit_remove_journal = 1;
no_valid_journal:
if (commit_remove_journal == 0) {
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);
2003-12-07 14:28:50 +08:00
fputs(_("Journal removed\n"), stdout);
free(journal_path);
return 0;
}
/* Helper function for remove_journal_inode */
static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
blk64_t ref_block EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
2003-12-07 14:28:50 +08:00
void *private EXT2FS_ATTR((unused)))
{
blk64_t block;
int group;
block = *blocknr;
ext2fs_unmark_block_bitmap2(fs->block_map, block);
group = ext2fs_group_of_blk2(fs, block);
ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1);
ext2fs_group_desc_csum_set(fs, group);
ext2fs_free_blocks_count_add(fs->super, EXT2FS_CLUSTER_RATIO(fs));
return 0;
}
/*
* Remove the journal inode from the filesystem
*/
static errcode_t 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, "%s",
_("while reading journal inode"));
return retval;
}
if (ino == EXT2_JOURNAL_INO) {
retval = ext2fs_read_bitmaps(fs);
if (retval) {
com_err(program_name, retval, "%s",
_("while reading bitmaps"));
return retval;
}
retval = ext2fs_block_iterate3(fs, ino,
BLOCK_FLAG_READ_ONLY, NULL,
release_blocks_proc, NULL);
if (retval) {
com_err(program_name, retval, "%s",
_("while clearing journal inode"));
return retval;
}
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, "%s",
_("while writing journal inode"));
return retval;
}
fs->super->s_journal_inum = 0;
memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
ext2fs_mark_super_dirty(fs);
return 0;
}
/*
* Update the default mount options
*/
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);
return 1;
}
ext2fs_mark_super_dirty(fs);
return 0;
}
static void check_fsck_needed(ext2_filsys fs, const char *prompt)
{
/* Refuse to modify anything but a freshly checked valid filesystem. */
if (!(fs->super->s_state & EXT2_VALID_FS) ||
(fs->super->s_state & EXT2_ERROR_FS) ||
(fs->super->s_lastcheck < fs->super->s_mtime)) {
printf("\n%s\n", _(please_fsck));
if (mount_flags & EXT2_MF_READONLY)
printf("%s", _("(and reboot afterwards!)\n"));
exit(1);
}
/* Give the admin a few seconds to bail out of a dangerous op. */
if (!getenv("TUNE2FS_FORCE_PROMPT") && (!isatty(0) || !isatty(1)))
return;
puts(prompt);
proceed_question(5);
}
static void request_dir_fsck_afterwards(ext2_filsys fs)
{
static int requested;
if (requested++)
return;
fsck_requested++;
fs->super->s_state &= ~EXT2_VALID_FS;
printf("\n%s\n", _(please_dir_fsck));
if (mount_flags & EXT2_MF_READONLY)
printf("%s", _("(and reboot afterwards!)\n"));
}
static void request_fsck_afterwards(ext2_filsys fs)
{
static int requested = 0;
if (requested++)
return;
fsck_requested++;
fs->super->s_state &= ~EXT2_VALID_FS;
printf("\n%s\n", _(please_fsck));
if (mount_flags & EXT2_MF_READONLY)
printf("%s", _("(and reboot afterwards!)\n"));
}
static void convert_64bit(ext2_filsys fs, int direction)
{
if (!direction)
return;
/*
* Is resize2fs going to demand a fsck run? Might as well tell the
* user now.
*/
if (!fsck_requested &&
((fs->super->s_state & EXT2_ERROR_FS) ||
!(fs->super->s_state & EXT2_VALID_FS) ||
fs->super->s_lastcheck < fs->super->s_mtime))
request_fsck_afterwards(fs);
if (fsck_requested)
fprintf(stderr, _("After running e2fsck, please run `resize2fs %s %s"),
direction > 0 ? "-b" : "-s", fs->device_name);
else
fprintf(stderr, _("Please run `resize2fs %s %s"),
direction > 0 ? "-b" : "-s", fs->device_name);
if (undo_file)
fprintf(stderr, _(" -z \"%s\""), undo_file);
if (direction > 0)
fprintf(stderr, _("' to enable 64-bit mode.\n"));
else
fprintf(stderr, _("' to disable 64-bit mode.\n"));
}
/* Rewrite extents */
static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode)
{
ext2_extent_handle_t handle;
struct ext2fs_extent extent;
errcode_t errcode;
struct ext2_extent_info info;
if (!(inode->i_flags & EXT4_EXTENTS_FL) ||
!ext2fs_has_feature_metadata_csum(fs->super))
return 0;
errcode = ext2fs_extent_open(fs, ino, &handle);
if (errcode)
return errcode;
errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
if (errcode)
goto out;
do {
errcode = ext2fs_extent_get_info(handle, &info);
if (errcode)
break;
/*
* If this is the first extent in an extent block that we
* haven't visited, rewrite the extent to force the ETB
* checksum to be rewritten.
*/
if (info.curr_entry == 1 && info.curr_level != 0 &&
!(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)) {
errcode = ext2fs_extent_replace(handle, 0, &extent);
if (errcode)
break;
}
/* Skip to the end of a block of leaf nodes */
if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
errcode = ext2fs_extent_get(handle,
EXT2_EXTENT_LAST_SIB,
&extent);
if (errcode)
break;
}
errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
} while (errcode == 0);
out:
/* Ok if we run off the end */
if (errcode == EXT2_ET_EXTENT_NO_NEXT)
errcode = 0;
ext2fs_extent_free(handle);
return errcode;
}
/*
* Rewrite directory blocks with checksums
*/
struct rewrite_dir_context {
char *buf;
errcode_t errcode;
ext2_ino_t dir;
int is_htree;
};
static int rewrite_dir_block(ext2_filsys fs,
blk64_t *blocknr,
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
blk64_t ref_block EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
void *priv_data)
{
struct ext2_dx_countlimit *dcl = NULL;
struct rewrite_dir_context *ctx = priv_data;
int dcl_offset, changed = 0;
ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;
/* if htree node... */
if (ctx->is_htree)
ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
&dcl, &dcl_offset);
if (dcl) {
if (!ext2fs_has_feature_metadata_csum(fs->super)) {
/* Ensure limit is the max size */
int max_entries = (fs->blocksize - dcl_offset) /
sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
changed = 1;
dcl->limit = ext2fs_cpu_to_le16(max_entries);
}
} else {
/* If htree block is full then rebuild the dir */
if (ext2fs_le16_to_cpu(dcl->count) ==
ext2fs_le16_to_cpu(dcl->limit)) {
request_dir_fsck_afterwards(fs);
return 0;
}
/*
* Ensure dcl->limit is small enough to leave room for
* the checksum tail.
*/
int max_entries = (fs->blocksize - (dcl_offset +
sizeof(struct ext2_dx_tail))) /
sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
dcl->limit = ext2fs_cpu_to_le16(max_entries);
/* Always rewrite checksum */
changed = 1;
}
} else {
unsigned int rec_len, name_size;
char *top = ctx->buf + fs->blocksize;
struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
/* Find last and penultimate dirent */
while ((char *)de < top) {
penultimate_de = last_de;
last_de = de;
ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
if (!ctx->errcode && !rec_len)
ctx->errcode = EXT2_ET_DIR_CORRUPTED;
if (ctx->errcode)
return BLOCK_ABORT;
de = (struct ext2_dir_entry *)(((char *)de) + rec_len);
}
ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
if (ctx->errcode)
return BLOCK_ABORT;
name_size = ext2fs_dirent_name_len(last_de);
if (!ext2fs_has_feature_metadata_csum(fs->super)) {
if (!penultimate_de)
return 0;
if (last_de->inode ||
name_size ||
rec_len != sizeof(struct ext2_dir_entry_tail))
return 0;
/*
* The last dirent is unused and the right length to
* have stored a checksum. Erase it.
*/
ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
&rec_len);
if (!rec_len)
ctx->errcode = EXT2_ET_DIR_CORRUPTED;
if (ctx->errcode)
return BLOCK_ABORT;
ext2fs_set_rec_len(fs, rec_len +
sizeof(struct ext2_dir_entry_tail),
penultimate_de);
changed = 1;
} else {
unsigned csum_size = sizeof(struct ext2_dir_entry_tail);
struct ext2_dir_entry_tail *t;
/*
* If the last dirent looks like the tail, just update
* the checksum.
*/
if (!last_de->inode &&
rec_len == csum_size) {
t = (struct ext2_dir_entry_tail *)last_de;
t->det_reserved_name_len =
EXT2_DIR_NAME_LEN_CSUM;
changed = 1;
goto out;
}
if (name_size & 3)
name_size = (name_size & ~3) + 4;
/* If there's not enough space for the tail, e2fsck */
if (rec_len <= (8 + name_size + csum_size)) {
request_dir_fsck_afterwards(fs);
return 0;
}
/* Shorten that last de and insert the tail */
ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize);
ext2fs_initialize_dirent_tail(fs, t);
/* Always update checksum */
changed = 1;
}
}
out:
if (!changed)
return 0;
ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
0, ctx->dir);
if (ctx->errcode)
return BLOCK_ABORT;
return 0;
}
static errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
struct ext2_inode *inode)
{
errcode_t retval;
struct rewrite_dir_context ctx;
retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
if (retval)
return retval;
ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
ctx.dir = dir;
ctx.errcode = 0;
retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
BLOCK_FLAG_DATA_ONLY,
0, rewrite_dir_block, &ctx);
ext2fs_free_mem(&ctx.buf);
if (retval)
return retval;
return ctx.errcode;
}
/*
* Forcibly set checksums in all inodes.
*/
static void rewrite_inodes(ext2_filsys fs)
{
int length = EXT2_INODE_SIZE(fs->super);
struct ext2_inode *inode, *zero;
char *ea_buf;
ext2_inode_scan scan;
errcode_t retval;
ext2_ino_t ino;
blk64_t file_acl_block;
int inode_dirty;
if (fs->super->s_creator_os != EXT2_OS_LINUX)
return;
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval) {
com_err("set_csum", retval, "while opening inode scan");
exit(1);
}
retval = ext2fs_get_mem(length, &inode);
if (retval) {
com_err("set_csum", retval, "while allocating memory");
exit(1);
}
retval = ext2fs_get_memzero(length, &zero);
if (retval) {
com_err("set_csum", retval, "while allocating memory");
exit(1);
}
retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
if (retval) {
com_err("set_csum", retval, "while allocating memory");
exit(1);
}
do {
retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
if (retval) {
com_err("set_csum", retval, "while getting next inode");
exit(1);
}
if (!ino)
break;
if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
inode_dirty = 1;
} else {
if (memcmp(inode, zero, length) != 0) {
memset(inode, 0, length);
inode_dirty = 1;
} else {
inode_dirty = 0;
}
}
if (inode_dirty) {
retval = ext2fs_write_inode_full(fs, ino, inode,
length);
if (retval) {
com_err("set_csum", retval, "while writing "
"inode");
exit(1);
}
}
retval = rewrite_extents(fs, ino, inode);
if (retval) {
com_err("rewrite_extents", retval,
"while rewriting extents");
exit(1);
}
if (LINUX_S_ISDIR(inode->i_mode) &&
ext2fs_inode_has_valid_blocks2(fs, inode)) {
retval = rewrite_directory(fs, ino, inode);
if (retval) {
com_err("rewrite_directory", retval,
"while rewriting directories");
exit(1);
}
}
file_acl_block = ext2fs_file_acl_block(fs, inode);
if (!file_acl_block)
continue;
retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
if (retval) {
com_err("rewrite_eablock", retval,
"while rewriting extended attribute");
exit(1);
}
retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
ino);
if (retval) {
com_err("rewrite_eablock", retval,
"while rewriting extended attribute");
exit(1);
}
} while (ino);
ext2fs_free_mem(&zero);
ext2fs_free_mem(&inode);
ext2fs_free_mem(&ea_buf);
ext2fs_close_inode_scan(scan);
}
static void rewrite_metadata_checksums(ext2_filsys fs)
{
errcode_t retval;
dgrp_t i;
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
ext2fs_init_csum_seed(fs);
for (i = 0; i < fs->group_desc_count; i++)
ext2fs_group_desc_csum_set(fs, i);
retval = ext2fs_read_bitmaps(fs);
if (retval) {
com_err("rewrite_metadata_checksums", retval,
"while reading bitmaps");
exit(1);
}
rewrite_inodes(fs);
ext2fs_mark_ib_dirty(fs);
ext2fs_mark_bb_dirty(fs);
ext2fs_mmp_update2(fs, 1);
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (ext2fs_has_feature_metadata_csum(fs->super))
fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
else
fs->super->s_checksum_type = 0;
ext2fs_mark_super_dirty(fs);
}
static void enable_uninit_bg(ext2_filsys fs)
{
struct ext2_group_desc *gd;
dgrp_t i;
for (i = 0; i < fs->group_desc_count; i++) {
gd = ext2fs_group_desc(fs, fs->group_desc, i);
gd->bg_itable_unused = 0;
gd->bg_flags = EXT2_BG_INODE_ZEROED;
ext2fs_group_desc_csum_set(fs, i);
}
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
static errcode_t zero_empty_inodes(ext2_filsys fs)
{
int length = EXT2_INODE_SIZE(fs->super);
struct ext2_inode *inode = NULL;
ext2_inode_scan scan;
errcode_t retval;
ext2_ino_t ino;
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval)
goto out;
retval = ext2fs_get_mem(length, &inode);
if (retval)
goto out;
do {
retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
if (retval)
goto out;
if (!ino)
break;
if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
memset(inode, 0, length);
retval = ext2fs_write_inode_full(fs, ino, inode,
length);
if (retval)
goto out;
}
} while (1);
out:
ext2fs_free_mem(&inode);
ext2fs_close_inode_scan(scan);
return retval;
}
static errcode_t disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag)
{
struct ext2_group_desc *gd;
dgrp_t i;
errcode_t retval;
blk64_t b, c, d;
/* Load bitmaps to ensure that the uninit ones get written out */
fs->super->s_feature_ro_compat |= csum_feature_flag;
retval = ext2fs_read_bitmaps(fs);
fs->super->s_feature_ro_compat &= ~csum_feature_flag;
if (retval) {
com_err("disable_uninit_bg", retval,
"while reading bitmaps");
request_fsck_afterwards(fs);
return retval;
}
ext2fs_mark_ib_dirty(fs);
ext2fs_mark_bb_dirty(fs);
/* If we're only turning off uninit_bg, zero the inodes */
if (csum_feature_flag == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
retval = zero_empty_inodes(fs);
if (retval) {
com_err("disable_uninit_bg", retval,
"while zeroing unused inodes");
request_fsck_afterwards(fs);
return retval;
}
}
/* The bbitmap is zeroed; we must mark group metadata blocks in use */
for (i = 0; i < fs->group_desc_count; i++) {
b = ext2fs_block_bitmap_loc(fs, i);
ext2fs_mark_block_bitmap2(fs->block_map, b);
b = ext2fs_inode_bitmap_loc(fs, i);
ext2fs_mark_block_bitmap2(fs->block_map, b);
retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
if (retval == 0 && b)
ext2fs_mark_block_bitmap2(fs->block_map, b);
if (retval == 0 && c)
ext2fs_mark_block_bitmap2(fs->block_map, c);
if (retval == 0 && d)
ext2fs_mark_block_bitmap2(fs->block_map, d);
if (retval) {
com_err("disable_uninit_bg", retval,
"while initializing block bitmaps");
request_fsck_afterwards(fs);
}
gd = ext2fs_group_desc(fs, fs->group_desc, i);
gd->bg_itable_unused = 0;
gd->bg_flags = 0;
ext2fs_group_desc_csum_set(fs, i);
}
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
ext2fs_mark_super_dirty(fs);
return 0;
}
static void
try_confirm_csum_seed_support(void)
{
if (access("/sys/fs/ext4/features/metadata_csum_seed", R_OK))
fputs(_("WARNING: Could not confirm kernel support for "
"metadata_csum_seed.\n This requires Linux >= "
"v4.4.\n"), stderr);
}
/*
* Update the feature set as provided by the user.
*/
static int update_feature_set(ext2_filsys fs, char *features)
{
struct ext2_super_block *sb = fs->super;
__u32 old_features[3];
int type_err;
unsigned int mask_err;
errcode_t err;
enum quota_type qtype;
#define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
((&sb->s_feature_compat)[(type)] & (mask)))
#define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
!((&sb->s_feature_compat)[(type)] & (mask)))
#define FEATURE_CHANGED(type, mask) ((mask) & \
(old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
if (e2p_edit_feature2(features, &sb->s_feature_compat,
ok_features, clear_ok_features,
&type_err, &mask_err)) {
if (!mask_err)
fprintf(stderr,
_("Invalid filesystem option set: %s\n"),
features);
else if (type_err & E2P_FEATURE_NEGATE_FLAG)
fprintf(stderr, _("Clearing filesystem feature '%s' "
"not supported.\n"),
e2p_feature2string(type_err &
E2P_FEATURE_TYPE_MASK,
mask_err));
else
fprintf(stderr, _("Setting filesystem feature '%s' "
"not supported.\n"),
e2p_feature2string(type_err, mask_err));
return 1;
}
if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
if ((mount_flags & EXT2_MF_MOUNTED) &&
!(mount_flags & EXT2_MF_READONLY)) {
fputs(_("The has_journal feature may only be "
2003-12-07 14:28:50 +08:00
"cleared when the filesystem is\n"
"unmounted or mounted "
"read-only.\n"), stderr);
return 1;
}
if (ext2fs_has_feature_journal_needs_recovery(sb) &&
f_flag < 2) {
2003-12-07 14:28:50 +08:00
fputs(_("The needs_recovery flag is set. "
"Please run e2fsck before clearing\n"
"the has_journal flag.\n"), stderr);
return 1;
}
if (sb->s_journal_inum) {
if (remove_journal_inode(fs))
return 1;
}
if (sb->s_journal_dev) {
if (remove_journal_device(fs))
return 1;
}
}
e2fsprogs: Disallow tune2fs enabling sparse_super with ext4 meta_bg enabled When meta_bg feature is enabled, group descriptor block is allocated every 128 block group (or every 64 block group if 64bit feature is enabled). In such situation, files in block group more than #128 will be removed if sparse_super feature is enabled with tune2fs and afterwards necessary e2fsck running. Because tune2fs does not reallocate group descriptor blocks but just set sparse_super feature. If ext4 has sparse_super, ext2fs_descriptor_block_loc2() called by e2fsck thinks the block group (e.g. #128) that it has group descriptor block at the head offset. But that offset is used as backup super block before. So e2fsck fixes ext4 based on invalid group descriptor blocks and this cause data lost. The patch avoids this problem simply by disallow tune2fs enabling sparse_super if meta_bg is enabled. Steps to reproduce: 1. Create ext4 which has meta_bg, ^sparse_super and 129+ block groups. # mke2fs -t ext4 -O meta_bg,^resize_inode,^sparse_super DEV 17G # mount DEV /MP 2. Create direcotry and files which use block group #128's metadata. # echo $((8192*128+1)) > /sys/fs/ext4/DEV/inode_goal # mkdir /MP/DIR # for i in $(seq 1 100); do dd if=/dev/urandom of=/MP/DIR/file$i bs=1024 count=10; done 3. Enable sparse_super with tune2fs then execute e2fsck. Data in block group #128 will be lost!! # umount DEV # tune2fs -O sparse_super DEV # e2fsck/e2fsck -yf DEV Signed-off-by: Akira Fujita <a-fujita@rs.jp.ne.cocm> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-02-07 04:11:52 +08:00
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
if (ext2fs_has_feature_meta_bg(sb)) {
e2fsprogs: Disallow tune2fs enabling sparse_super with ext4 meta_bg enabled When meta_bg feature is enabled, group descriptor block is allocated every 128 block group (or every 64 block group if 64bit feature is enabled). In such situation, files in block group more than #128 will be removed if sparse_super feature is enabled with tune2fs and afterwards necessary e2fsck running. Because tune2fs does not reallocate group descriptor blocks but just set sparse_super feature. If ext4 has sparse_super, ext2fs_descriptor_block_loc2() called by e2fsck thinks the block group (e.g. #128) that it has group descriptor block at the head offset. But that offset is used as backup super block before. So e2fsck fixes ext4 based on invalid group descriptor blocks and this cause data lost. The patch avoids this problem simply by disallow tune2fs enabling sparse_super if meta_bg is enabled. Steps to reproduce: 1. Create ext4 which has meta_bg, ^sparse_super and 129+ block groups. # mke2fs -t ext4 -O meta_bg,^resize_inode,^sparse_super DEV 17G # mount DEV /MP 2. Create direcotry and files which use block group #128's metadata. # echo $((8192*128+1)) > /sys/fs/ext4/DEV/inode_goal # mkdir /MP/DIR # for i in $(seq 1 100); do dd if=/dev/urandom of=/MP/DIR/file$i bs=1024 count=10; done 3. Enable sparse_super with tune2fs then execute e2fsck. Data in block group #128 will be lost!! # umount DEV # tune2fs -O sparse_super DEV # e2fsck/e2fsck -yf DEV Signed-off-by: Akira Fujita <a-fujita@rs.jp.ne.cocm> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-02-07 04:11:52 +08:00
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_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
/*
* If adding a journal flag, let the create journal
* code below handle setting the flag and creating the
* journal. We supply a default size if necessary.
*/
if (!journal_size)
journal_size = -1;
ext2fs_clear_feature_journal(sb);
}
if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
if (!sb->s_def_hash_version)
sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
if (uuid_is_null((unsigned char *) sb->s_hash_seed))
uuid_generate((unsigned char *) sb->s_hash_seed);
}
if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
if (ext2fs_check_desc(fs)) {
fputs(_("Clearing the flex_bg flag would "
"cause the the filesystem to be\n"
"inconsistent.\n"), stderr);
return 1;
}
}
if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
if ((mount_flags & EXT2_MF_MOUNTED) &&
!(mount_flags & EXT2_MF_READONLY)) {
fputs(_("The huge_file feature may only be "
"cleared when the filesystem is\n"
"unmounted or mounted "
"read-only.\n"), stderr);
return 1;
}
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
check_fsck_needed(fs,
_("Enabling checksums could take some time."));
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("Cannot enable metadata_csum on a mounted "
"filesystem!\n"), stderr);
exit(1);
}
if (!ext2fs_has_feature_extents(fs->super))
printf("%s",
_("Extents are not enabled. The file extent "
"tree can be checksummed, whereas block maps "
"cannot. Not enabling extents reduces the "
"coverage of metadata checksumming. "
"Re-run with -O extent to rectify.\n"));
if (!ext2fs_has_feature_64bit(fs->super))
printf("%s",
_("64-bit filesystem support is not enabled. "
"The larger fields afforded by this feature "
"enable full-strength checksumming. "
"Run resize2fs -b to rectify.\n"));
rewrite_checksums = 1;
/* metadata_csum supersedes uninit_bg */
ext2fs_clear_feature_gdt_csum(fs->super);
/* if uninit_bg was previously off, rewrite group desc */
if (!(old_features[E2P_FEATURE_RO_INCOMPAT] &
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
enable_uninit_bg(fs);
/*
* Since metadata_csum supersedes uninit_bg, pretend like
* uninit_bg has been off all along.
*/
old_features[E2P_FEATURE_RO_INCOMPAT] &=
~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
}
if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
__u32 test_features[3];
check_fsck_needed(fs,
_("Disabling checksums could take some time."));
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("Cannot disable metadata_csum on a mounted "
"filesystem!\n"), stderr);
exit(1);
}
rewrite_checksums = 1;
/* Enable uninit_bg unless the user expressly turned it off */
memcpy(test_features, old_features, sizeof(test_features));
test_features[E2P_FEATURE_RO_INCOMPAT] |=
EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
e2p_edit_feature2(features, test_features, ok_features,
clear_ok_features, NULL, NULL);
if (test_features[E2P_FEATURE_RO_INCOMPAT] &
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
ext2fs_set_feature_gdt_csum(fs->super);
/*
* If we're turning off metadata_csum and not turning on
* uninit_bg, rewrite group desc.
*/
if (!ext2fs_has_feature_gdt_csum(fs->super)) {
err = disable_uninit_bg(fs,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
if (err)
return 1;
} else
/*
* metadata_csum previously provided uninit_bg, so if
* we're also setting the uninit_bg feature bit,
* pretend like it was previously enabled. Checksums
* will be rewritten with crc16 later.
*/
old_features[E2P_FEATURE_RO_INCOMPAT] |=
EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
fs->super->s_checksum_seed = 0;
ext2fs_clear_feature_csum_seed(fs->super);
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
/* Do not enable uninit_bg when metadata_csum enabled */
if (ext2fs_has_feature_metadata_csum(fs->super))
ext2fs_clear_feature_gdt_csum(fs->super);
else
enable_uninit_bg(fs);
}
if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
err = disable_uninit_bg(fs,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
if (err)
return 1;
}
/*
* We don't actually toggle 64bit; resize2fs does that. But this
* must come after the metadata_csum feature_on so that it won't
* complain about the lack of 64bit.
*/
if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_64BIT)) {
if (mount_flags & EXT2_MF_MOUNTED) {
fprintf(stderr, _("Cannot enable 64-bit mode "
"while mounted!\n"));
exit(1);
}
ext2fs_clear_feature_64bit(sb);
feature_64bit = 1;
}
if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_64BIT)) {
if (mount_flags & EXT2_MF_MOUNTED) {
fprintf(stderr, _("Cannot disable 64-bit mode "
"while mounted!\n"));
exit(1);
}
ext2fs_set_feature_64bit(sb);
feature_64bit = -1;
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
/*
* Set the Q_flag here and handle the quota options in the code
* below.
*/
if (!Q_flag) {
Q_flag = 1;
/* Enable usr/grp quota by default */
for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
if (qtype != PRJQUOTA)
quota_enable[qtype] = QOPT_ENABLE;
else
quota_enable[qtype] = QOPT_DISABLE;
}
}
ext2fs_clear_feature_quota(sb);
}
if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_PROJECT)) {
if (!Q_flag && !ext2fs_has_feature_quota(sb))
fputs(_("\nWarning: enabled project without quota together\n"),
stderr);
Q_flag = 1;
quota_enable[PRJQUOTA] = QOPT_ENABLE;
}
if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
/*
* Set the Q_flag here and handle the quota options in the code
* below.
*/
if (Q_flag)
fputs(_("\nWarning: '^quota' option overrides '-Q'"
"arguments.\n"), stderr);
Q_flag = 1;
/* Disable all quota by default */
for (qtype = 0; qtype < MAXQUOTAS; qtype++)
quota_enable[qtype] = QOPT_DISABLE;
}
if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
fs->super->s_encrypt_algos[0] =
EXT4_ENCRYPTION_MODE_AES_256_XTS;
fs->super->s_encrypt_algos[1] =
EXT4_ENCRYPTION_MODE_AES_256_CTS;
}
if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
if (!ext2fs_has_feature_metadata_csum(sb)) {
fputs(_("Setting feature 'metadata_csum_seed' "
"is only supported\non filesystems with "
"the metadata_csum feature enabled.\n"),
stderr);
return 1;
}
try_confirm_csum_seed_support();
fs->super->s_checksum_seed = fs->csum_seed;
}
if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
__le32 uuid_seed;
uuid_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
sizeof(fs->super->s_uuid));
if (fs->super->s_checksum_seed != uuid_seed &&
(mount_flags & EXT2_MF_MOUNTED)) {
fputs(_("UUID has changed since enabling "
"metadata_csum. Filesystem must be unmounted "
"\nto safely rewrite all metadata to "
"match the new UUID.\n"), stderr);
return 1;
}
rewrite_checksums = 1;
}
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 (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
EXT2_FEATURE_INCOMPAT_FILETYPE) ||
FEATURE_CHANGED(E2P_FEATURE_COMPAT,
EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
EXT2_FEATURE_RO_COMPAT_LARGE_FILE))
request_fsck_afterwards(fs);
if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
(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 int add_journal(ext2_filsys fs)
{
unsigned long journal_blocks;
errcode_t retval;
ext2_filsys jfs;
io_manager io_ptr;
if (ext2fs_has_feature_journal(fs->super)) {
2003-12-07 14:28:50 +08:00
fputs(_("The filesystem already has a journal.\n"), stderr);
goto err;
}
if (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")) {
io_ptr = test_io_manager;
test_io_backing_manager = unix_io_manager;
} else
#endif
io_ptr = unix_io_manager;
retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
EXT2_FLAG_JOURNAL_DEV_OK, 0,
fs->blocksize, io_ptr, &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_free(&jfs);
if (retval) {
com_err(program_name, retval,
_("while adding filesystem to journal on %s"),
journal_device);
goto err;
}
2003-12-07 14:28:50 +08:00
fputs(_("done\n"), stdout);
} else if (journal_size) {
2003-12-07 14:28:50 +08:00
fputs(_("Creating journal inode: "), stdout);
fflush(stdout);
journal_blocks = figure_journal_size(journal_size, fs);
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, "%s",
_("\n\twhile trying to create journal file"));
return retval;
} else
2003-12-07 14:28:50 +08:00
fputs(_("done\n"), stdout);
/*
* 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->super->s_max_mnt_count,
fs->super->s_checkinterval);
return 0;
err:
free(journal_device);
return 1;
}
static void handle_quota_options(ext2_filsys fs)
{
errcode_t retval;
quota_ctx_t qctx;
ext2_ino_t qf_ino;
enum quota_type qtype;
int enable = 0;
for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
if (quota_enable[qtype] != 0)
break;
if (qtype == MAXQUOTAS)
/* Nothing to do. */
return;
retval = quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
if (retval) {
com_err(program_name, retval,
_("while initializing quota context in support library"));
exit(1);
}
for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
if (quota_enable[qtype] == QOPT_ENABLE) {
enable = 1;
break;
}
}
if (enable)
quota_compute_usage(qctx);
for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
if (quota_enable[qtype] == QOPT_ENABLE &&
*quota_sb_inump(fs->super, qtype) == 0) {
if ((qf_ino = quota_file_exists(fs, qtype)) > 0) {
retval = quota_update_limits(qctx, qf_ino,
qtype);
if (retval) {
com_err(program_name, retval,
_("while updating quota limits (%d)"),
qtype);
exit(1);
}
}
retval = quota_write_inode(qctx, 1 << qtype);
if (retval) {
com_err(program_name, retval,
_("while writing quota file (%d)"),
qtype);
exit(1);
}
} else if (quota_enable[qtype] == QOPT_DISABLE) {
retval = quota_remove_inode(fs, qtype);
if (retval) {
com_err(program_name, retval,
_("while removing quota file (%d)"),
qtype);
exit(1);
}
}
}
quota_release_context(&qctx);
if (enable) {
ext2fs_set_feature_quota(fs->super);
ext2fs_mark_super_dirty(fs);
} else {
for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
if (*quota_sb_inump(fs->super, qtype) != 0)
break;
if (qtype == MAXQUOTAS) {
fs->super->s_feature_ro_compat &=
~EXT4_FEATURE_RO_COMPAT_QUOTA;
ext2fs_mark_super_dirty(fs);
}
}
return;
}
static int option_handle_function(char *token, void *data)
{
if (strncmp(token, "usr", 3) == 0) {
quota_enable[USRQUOTA] = QOPT_ENABLE;
} else if (strncmp(token, "^usr", 4) == 0) {
quota_enable[USRQUOTA] = QOPT_DISABLE;
} else if (strncmp(token, "grp", 3) == 0) {
quota_enable[GRPQUOTA] = QOPT_ENABLE;
} else if (strncmp(token, "^grp", 4) == 0) {
quota_enable[GRPQUOTA] = QOPT_DISABLE;
} else if (strncmp(token, "prj", 3) == 0) {
quota_enable[PRJQUOTA] = QOPT_ENABLE;
} else if (strncmp(token, "^prj", 4) == 0) {
quota_enable[PRJQUOTA] = QOPT_DISABLE;
} else {
fputs(_("\nBad quota options specified.\n\n"
"Following valid quota options are available "
"(pass by separating with comma):\n"
"\t[^]usr[quota]\n"
"\t[^]grp[quota]\n"
"\t[^]prj[quota]\n"
"\n\n"), stderr);
return 1;
}
return 0;
}
static void parse_e2label_options(int argc, char ** argv)
{
if ((argc < 2) || (argc > 3)) {
2003-12-07 14:28:50 +08:00
fputs(_("Usage: e2label device [newlabel]\n"), stderr);
exit(1);
}
io_options = strchr(argv[1], '?');
if (io_options)
*io_options++ = 0;
device_name = blkid_get_devname(NULL, argv[1], NULL);
if (!device_name) {
com_err("e2label", 0, _("Unable to resolve '%s'"),
argv[1]);
exit(1);
}
open_flag = EXT2_FLAG_JOURNAL_DEV_OK;
if (argc == 3) {
open_flag |= EXT2_FLAG_RW;
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));
#ifdef HAVE_STRPTIME
strptime(str, "%Y%m%d%H%M%S", &ts);
#else
sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
&ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
ts.tm_year -= 1900;
ts.tm_mon -= 1;
if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
ts.tm_min > 59 || ts.tm_sec > 61)
ts.tm_mday = 0;
#endif
if (ts.tm_mday == 0) {
com_err(program_name, 0,
_("Couldn't parse date/time specifier: %s"),
str);
usage();
}
ts.tm_isdst = -1;
return (mktime(&ts));
}
static void parse_tune2fs_options(int argc, char **argv)
1997-04-26 21:21:57 +08:00
{
int c;
char *tmp;
struct group *gr;
struct passwd *pw;
int ret;
char optstring[100] = "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:z:Q:";
1997-04-26 21:21:57 +08:00
open_flag = 0;
printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
while ((c = getopt(argc, argv, optstring)) != EOF)
switch (c) {
case 'c':
max_mount_count = strtol(optarg, &tmp, 0);
if (*tmp || max_mount_count > 16000) {
com_err(program_name, 0,
_("bad mounts count - %s"),
optarg);
usage();
}
if (max_mount_count == 0)
max_mount_count = -1;
c_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'C':
mount_count = strtoul(optarg, &tmp, 0);
if (*tmp || mount_count > 16000) {
com_err(program_name, 0,
_("bad mounts count - %s"),
optarg);
usage();
}
C_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'e':
if (strcmp(optarg, "continue") == 0)
errors = EXT2_ERRORS_CONTINUE;
else if (strcmp(optarg, "remount-ro") == 0)
errors = EXT2_ERRORS_RO;
else if (strcmp(optarg, "panic") == 0)
errors = EXT2_ERRORS_PANIC;
else {
com_err(program_name, 0,
_("bad error behavior - %s"),
optarg);
usage();
}
e_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'E':
extended_cmd = optarg;
open_flag |= EXT2_FLAG_RW;
break;
case 'f': /* Force */
f_flag++;
break;
case 'g':
resgid = strtoul(optarg, &tmp, 0);
if (*tmp) {
gr = getgrnam(optarg);
if (gr == NULL)
tmp = optarg;
else {
resgid = gr->gr_gid;
*tmp = 0;
1997-04-26 21:34:30 +08:00
}
}
if (*tmp) {
com_err(program_name, 0,
_("bad gid/group name - %s"),
optarg);
usage();
}
g_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'i':
interval = strtoul(optarg, &tmp, 0);
switch (*tmp) {
case 's':
tmp++;
1997-04-26 21:34:30 +08:00
break;
case '\0':
case 'd':
case 'D': /* days */
interval *= 86400;
if (*tmp != '\0')
1997-04-29 22:53:37 +08:00
tmp++;
break;
1997-04-26 21:21:57 +08:00
case 'm':
case 'M': /* months! */
interval *= 86400 * 30;
tmp++;
1997-04-26 21:21:57 +08:00
break;
case 'w':
case 'W': /* weeks */
interval *= 86400 * 7;
tmp++;
break;
}
if (*tmp) {
com_err(program_name, 0,
_("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 |
EXT2_FLAG_JOURNAL_DEV_OK;
break;
case 'm':
reserved_ratio = strtod(optarg, &tmp);
if (*tmp || reserved_ratio > 50 ||
reserved_ratio < 0) {
com_err(program_name, 0,
_("bad reserved block ratio - %s"),
optarg);
usage();
}
m_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'M':
new_last_mounted = optarg;
M_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 'o':
if (mntopts_cmd) {
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, "%s",
_("-O may only be specified once"));
usage();
}
features_cmd = optarg;
open_flag = EXT2_FLAG_RW;
break;
case 'Q':
Q_flag = 1;
ret = parse_quota_opts(optarg, option_handle_function,
NULL);
if (ret)
exit(1);
open_flag = EXT2_FLAG_RW;
break;
case 'r':
reserved_blocks = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(program_name, 0,
_("bad reserved blocks count - %s"),
optarg);
usage();
}
r_flag = 1;
open_flag = EXT2_FLAG_RW;
break;
case 's': /* Deprecated */
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) {
pw = getpwnam(optarg);
1997-04-26 21:34:30 +08:00
if (pw == NULL)
tmp = optarg;
1997-04-26 22:00:26 +08:00
else {
1997-04-26 21:34:30 +08:00
resuid = pw->pw_uid;
1997-04-26 22:00:26 +08:00
*tmp = 0;
}
1997-04-26 21:34:30 +08:00
}
if (*tmp) {
com_err(program_name, 0,
_("bad uid/user name - %s"),
optarg);
usage();
1997-04-26 21:34:30 +08:00
}
u_flag = 1;
1997-04-29 22:53:37 +08:00
open_flag = EXT2_FLAG_RW;
break;
case 'U':
new_UUID = optarg;
U_flag = 1;
open_flag = EXT2_FLAG_RW |
EXT2_FLAG_JOURNAL_DEV_OK;
break;
case 'I':
new_inode_size = strtoul(optarg, &tmp, 0);
if (*tmp) {
com_err(program_name, 0,
_("bad inode size - %s"),
optarg);
usage();
}
if (!((new_inode_size &
(new_inode_size - 1)) == 0)) {
com_err(program_name, 0,
_("Inode size must be a "
"power of two- %s"),
optarg);
usage();
}
open_flag = EXT2_FLAG_RW;
I_flag = 1;
break;
case 'z':
undo_file = optarg;
break;
default:
usage();
1997-04-26 21:21:57 +08:00
}
if (optind < argc - 1 || optind == argc)
usage();
1997-04-29 22:53:37 +08:00
if (!open_flag && !l_flag)
usage();
io_options = strchr(argv[optind], '?');
if (io_options)
*io_options++ = 0;
device_name = blkid_get_devname(NULL, argv[optind], NULL);
if (!device_name) {
com_err(program_name, 0, _("Unable to resolve '%s'"),
argv[optind]);
exit(1);
}
}
#ifdef CONFIG_BUILD_FINDFS
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=<label>|UUID=<uuid>\n");
exit(2);
}
dev = blkid_get_devname(NULL, argv[1], NULL);
if (!dev) {
com_err("findfs", 0, _("Unable to resolve '%s'"),
argv[1]);
exit(1);
}
puts(dev);
exit(0);
}
#endif
static int parse_extended_opts(ext2_filsys fs, const char *opts)
{
char *buf, *token, *next, *p, *arg;
int len, hash_alg;
int r_usage = 0;
len = strlen(opts);
buf = malloc(len+1);
if (!buf) {
fprintf(stderr, "%s",
_("Couldn't allocate memory to parse options!\n"));
return 1;
}
strcpy(buf, opts);
for (token = buf; token && *token; token = next) {
p = strchr(token, ',');
next = 0;
if (p) {
*p = 0;
next = p+1;
}
arg = strchr(token, '=');
if (arg) {
*arg = 0;
arg++;
}
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);
} else if (!strcmp(token, "^test_fs")) {
fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
printf("Clearing test filesystem flag\n");
ext2fs_mark_super_dirty(fs);
} else if (strcmp(token, "stride") == 0) {
if (!arg) {
r_usage++;
continue;
}
stride = strtoul(arg, &p, 0);
if (*p) {
fprintf(stderr,
_("Invalid RAID stride: %s\n"),
arg);
r_usage++;
continue;
}
stride_set = 1;
} else if (strcmp(token, "stripe-width") == 0 ||
strcmp(token, "stripe_width") == 0) {
if (!arg) {
r_usage++;
continue;
}
stripe_width = strtoul(arg, &p, 0);
if (*p) {
fprintf(stderr,
_("Invalid RAID stripe-width: %s\n"),
arg);
r_usage++;
continue;
}
stripe_width_set = 1;
} else if (strcmp(token, "hash_alg") == 0 ||
strcmp(token, "hash-alg") == 0) {
if (!arg) {
r_usage++;
continue;
}
hash_alg = e2p_string2hash(arg);
if (hash_alg < 0) {
fprintf(stderr,
_("Invalid hash algorithm: %s\n"),
arg);
r_usage++;
continue;
}
fs->super->s_def_hash_version = hash_alg;
printf(_("Setting default hash algorithm "
"to %s (%d)\n"),
arg, hash_alg);
ext2fs_mark_super_dirty(fs);
} else if (!strcmp(token, "mount_opts")) {
if (!arg) {
r_usage++;
continue;
}
if (strlen(arg) >= sizeof(fs->super->s_mount_opts)) {
fprintf(stderr,
"Extended mount options too long\n");
continue;
}
ext_mount_opts = strdup(arg);
} else
r_usage++;
}
if (r_usage) {
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=<hash algorithm>\n"
"\tmount_opts=<extended default mount options>\n"
"\tstride=<RAID per-disk chunk size in blocks>\n"
"\tstripe_width=<RAID stride*data disks in blocks>\n"
"\ttest_fs\n"
"\t^test_fs\n"));
free(buf);
return 1;
}
free(buf);
return 0;
}
/*
* Fill in the block bitmap bmap with the information regarding the
* blocks to be moved
*/
static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp,
ext2fs_block_bitmap bmap)
{
dgrp_t i;
int retval;
ext2_badblocks_list bb_list = 0;
blk64_t j, needed_blocks = 0;
blk64_t start_blk, end_blk;
retval = ext2fs_read_bb_inode(fs, &bb_list);
if (retval)
return retval;
for (i = 0; i < fs->group_desc_count; i++) {
start_blk = ext2fs_inode_table_loc(fs, i) +
fs->inode_blocks_per_group;
end_blk = ext2fs_inode_table_loc(fs, i) +
new_ino_blks_per_grp;
for (j = start_blk; j < end_blk; j++) {
if (ext2fs_test_block_bitmap2(fs->block_map, j)) {
/*
* IF the block is a bad block we fail
*/
if (ext2fs_badblocks_list_test(bb_list, j)) {
ext2fs_badblocks_list_free(bb_list);
return ENOSPC;
}
ext2fs_mark_block_bitmap2(bmap, j);
} else {
/*
* We are going to use this block for
* inode table. So mark them used.
*/
ext2fs_mark_block_bitmap2(fs->block_map, j);
}
}
needed_blocks += end_blk - start_blk;
}
ext2fs_badblocks_list_free(bb_list);
if (needed_blocks > ext2fs_free_blocks_count(fs->super))
return ENOSPC;
return 0;
}
static int ext2fs_is_meta_block(ext2_filsys fs, blk64_t blk)
{
dgrp_t group;
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)
return 1;
return 0;
}
static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk64_t blk)
{
blk64_t start_blk, end_blk;
start_blk = fs->super->s_first_data_block +
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
*/
end_blk = start_blk + EXT2_BLOCKS_PER_GROUP(fs->super);
if (blk >= start_blk && blk <= end_blk)
return 1;
return 0;
}
static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap)
{
char *buf;
dgrp_t group = 0;
errcode_t retval;
int meta_data = 0;
blk64_t blk, new_blk, goal;
struct blk_move *bmv;
retval = ext2fs_get_mem(fs->blocksize, &buf);
if (retval)
return retval;
for (new_blk = blk = fs->super->s_first_data_block;
blk < ext2fs_blocks_count(fs->super); blk++) {
if (!ext2fs_test_block_bitmap2(bmap, blk))
continue;
if (ext2fs_is_meta_block(fs, blk)) {
/*
* If the block is mapping a fs meta data block
* like group desc/block bitmap/inode bitmap. We
* should find a block in the same group and fix
* the respective fs metadata pointers. Otherwise
* fail
*/
group = ext2fs_group_of_blk2(fs, blk);
goal = ext2fs_group_first_block2(fs, group);
meta_data = 1;
} else {
goal = new_blk;
}
retval = ext2fs_new_block2(fs, goal, NULL, &new_blk);
if (retval)
goto err_out;
/* new fs meta data block should be in the same group */
if (meta_data && !ext2fs_is_block_in_group(fs, group, new_blk)) {
retval = ENOSPC;
goto err_out;
}
/* Mark this block as allocated */
ext2fs_mark_block_bitmap2(fs->block_map, new_blk);
/* Add it to block move list */
retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv);
if (retval)
goto err_out;
bmv->old_loc = blk;
bmv->new_loc = new_blk;
list_add(&(bmv->list), &blk_move_list);
retval = io_channel_read_blk64(fs->io, blk, 1, buf);
if (retval)
goto err_out;
retval = io_channel_write_blk64(fs->io, new_blk, 1, buf);
if (retval)
goto err_out;
}
err_out:
ext2fs_free_mem(&buf);
return retval;
}
static blk64_t translate_block(blk64_t blk)
{
struct list_head *entry;
struct blk_move *bmv;
list_for_each(entry, &blk_move_list) {
bmv = list_entry(entry, struct blk_move, list);
if (bmv->old_loc == blk)
return bmv->new_loc;
}
return 0;
}
static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)),
blk64_t *block_nr,
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
blk64_t ref_block EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
void *priv_data)
{
int ret = 0;
blk64_t new_blk;
ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data;
if (!ext2fs_test_block_bitmap2(bmap, *block_nr))
return 0;
new_blk = translate_block(*block_nr);
if (new_blk) {
*block_nr = new_blk;
/*
* This will force the ext2fs_write_inode in the iterator
*/
ret |= BLOCK_CHANGED;
}
return ret;
}
static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
{
errcode_t retval = 0;
ext2_ino_t ino;
blk64_t blk;
char *block_buf = 0;
struct ext2_inode inode;
ext2_inode_scan scan = NULL;
retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
if (retval)
return retval;
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval)
goto err_out;
while (1) {
retval = ext2fs_get_next_inode(scan, &ino, &inode);
if (retval)
goto err_out;
if (!ino)
break;
if (inode.i_links_count == 0)
continue; /* inode not in use */
/* FIXME!!
* If we end up modifying the journal inode
* the sb->s_jnl_blocks will differ. But a
* subsequent e2fsck fixes that.
* Do we need to fix this ??
*/
if (ext2fs_file_acl_block(fs, &inode) &&
ext2fs_test_block_bitmap2(bmap,
ext2fs_file_acl_block(fs, &inode))) {
blk = translate_block(ext2fs_file_acl_block(fs,
&inode));
if (!blk)
continue;
ext2fs_file_acl_block_set(fs, &inode, blk);
/*
* Write the inode to disk so that inode table
* resizing can work
*/
retval = ext2fs_write_inode(fs, ino, &inode);
if (retval)
goto err_out;
}
if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
continue;
retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
process_block, bmap);
if (retval)
goto err_out;
}
err_out:
ext2fs_free_mem(&block_buf);
ext2fs_close_inode_scan(scan);
return retval;
}
/*
* We need to scan for inode and block bitmaps that may need to be
* moved. This can take place if the filesystem was formatted for
* RAID arrays using the mke2fs's extended option "stride".
*/
static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
{
dgrp_t i;
blk64_t blk, new_blk;
for (i = 0; i < fs->group_desc_count; i++) {
blk = ext2fs_block_bitmap_loc(fs, i);
if (ext2fs_test_block_bitmap2(bmap, blk)) {
new_blk = translate_block(blk);
if (!new_blk)
continue;
ext2fs_block_bitmap_loc_set(fs, i, new_blk);
}
blk = ext2fs_inode_bitmap_loc(fs, i);
if (ext2fs_test_block_bitmap2(bmap, blk)) {
new_blk = translate_block(blk);
if (!new_blk)
continue;
ext2fs_inode_bitmap_loc_set(fs, i, new_blk);
}
}
return 0;
}
static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size)
{
dgrp_t i;
blk64_t blk;
errcode_t retval;
int new_ino_blks_per_grp;
unsigned int j;
char *old_itable = NULL, *new_itable = NULL;
char *tmp_old_itable = NULL, *tmp_new_itable = NULL;
unsigned long old_ino_size;
int old_itable_size, new_itable_size;
old_itable_size = fs->inode_blocks_per_group * fs->blocksize;
old_ino_size = EXT2_INODE_SIZE(fs->super);
new_ino_blks_per_grp = ext2fs_div_ceil(
EXT2_INODES_PER_GROUP(fs->super) *
new_ino_size,
fs->blocksize);
new_itable_size = new_ino_blks_per_grp * fs->blocksize;
retval = ext2fs_get_mem(old_itable_size, &old_itable);
if (retval)
return retval;
retval = ext2fs_get_mem(new_itable_size, &new_itable);
if (retval)
goto err_out;
tmp_old_itable = old_itable;
tmp_new_itable = new_itable;
for (i = 0; i < fs->group_desc_count; i++) {
blk = ext2fs_inode_table_loc(fs, i);
retval = io_channel_read_blk64(fs->io, blk,
fs->inode_blocks_per_group, old_itable);
if (retval)
goto err_out;
for (j = 0; j < EXT2_INODES_PER_GROUP(fs->super); j++) {
memcpy(new_itable, old_itable, old_ino_size);
memset(new_itable+old_ino_size, 0,
new_ino_size - old_ino_size);
new_itable += new_ino_size;
old_itable += old_ino_size;
}
/* reset the pointer */
old_itable = tmp_old_itable;
new_itable = tmp_new_itable;
retval = io_channel_write_blk64(fs->io, blk,
new_ino_blks_per_grp, new_itable);
if (retval)
goto err_out;
}
/* Update the meta data */
fs->inode_blocks_per_group = new_ino_blks_per_grp;
tune2fs: fix memory write overflow If we apply this patch 'e2fsprogs/tune2fs: rewrite metadata checksums when resizing inode size', we will trigger a segfault, this is because of the inode cache issues. Firstly we should notice that in expand_inode_table(), we have change the super block's s_inode_size to new inode size(for example, 256). Then we re-compute metadata checksums, see below code flow: |-->rewrite_metadata_checksums |----->rewrite_inodes |-------->ext2fs_write_inode_full In ext2fs_write_inode_full(), if an inode cache is hit, the below code will be executed: /* Check to see if the inode cache needs to be updated */ if (fs->icache) { for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { memcpy(fs->icache->cache[i].inode, inode, (bufsize > length) ? length : bufsize); break; } } } Before executing rewrite_inodes(), actually the inode in inode cache is allocated by old inode size(for example, 128), but here the memcpy will obviously write overflow, '(bufsize > length) ? length : bufsize' here will return 256(new inode size), so this is wrong, we need to fix this. I think we should call ext2fs_free_inode_cache() in expand_inode_table(), to drop the inode cache, because inode size has changed, if necessary, we will re-create this inode cache. Steps to reproduce this bug (apply 'tune2fs: rewrite metadata checksums when resizing inode size' first): dd if=/dev/zero of=file.img bs=1M count=128 device_name=$(/sbin/losetup -f) /sbin/losetup -f file.img mkfs.ext4 -I 128 -O ^flex_bg $device_name tune2fs -I 256 $device_name Signed-off-by: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-12-03 11:31:11 +08:00
ext2fs_free_inode_cache(fs->icache);
fs->icache = 0;
fs->super->s_inode_size = new_ino_size;
err_out:
if (old_itable)
ext2fs_free_mem(&old_itable);
if (new_itable)
ext2fs_free_mem(&new_itable);
return retval;
}
static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
{
blk64_t blk;
ext2_ino_t ino;
unsigned int group = 0;
unsigned int count = 0;
int total_free = 0;
int group_free = 0;
/*
* First calculate the block statistics
*/
for (blk = fs->super->s_first_data_block;
blk < ext2fs_blocks_count(fs->super); blk++) {
if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) {
group_free++;
total_free++;
}
count++;
if ((count == fs->super->s_blocks_per_group) ||
(blk == ext2fs_blocks_count(fs->super)-1)) {
ext2fs_bg_free_blocks_count_set(fs, group++,
group_free);
count = 0;
group_free = 0;
}
}
total_free = EXT2FS_C2B(fs, total_free);
ext2fs_free_blocks_count_set(fs->super, total_free);
/*
* Next, calculate the inode statistics
*/
group_free = 0;
total_free = 0;
count = 0;
group = 0;
/* Protect loop from wrap-around if s_inodes_count maxed */
for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) {
if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino)) {
group_free++;
total_free++;
}
count++;
if ((count == fs->super->s_inodes_per_group) ||
(ino == fs->super->s_inodes_count)) {
ext2fs_bg_free_inodes_count_set(fs, group++,
group_free);
count = 0;
group_free = 0;
}
}
fs->super->s_free_inodes_count = total_free;
ext2fs_mark_super_dirty(fs);
return 0;
}
#define list_for_each_safe(pos, pnext, head) \
for (pos = (head)->next, pnext = pos->next; pos != (head); \
pos = pnext, pnext = pos->next)
static void free_blk_move_list(void)
{
struct list_head *entry, *tmp;
struct blk_move *bmv;
list_for_each_safe(entry, tmp, &blk_move_list) {
bmv = list_entry(entry, struct blk_move, list);
list_del(entry);
ext2fs_free_mem(&bmv);
}
return;
}
static int resize_inode(ext2_filsys fs, unsigned long new_size)
{
errcode_t retval;
int new_ino_blks_per_grp;
ext2fs_block_bitmap bmap;
retval = ext2fs_read_inode_bitmap(fs);
if (retval) {
fputs(_("Failed to read inode bitmap\n"), stderr);
return retval;
}
retval = ext2fs_read_block_bitmap(fs);
if (retval) {
fputs(_("Failed to read block bitmap\n"), stderr);
return retval;
}
INIT_LIST_HEAD(&blk_move_list);
new_ino_blks_per_grp = ext2fs_div_ceil(
EXT2_INODES_PER_GROUP(fs->super)*
new_size,
fs->blocksize);
/* We may change the file system.
* Mark the file system as invalid so that
* the user is prompted to run fsck.
*/
fs->super->s_state &= ~EXT2_VALID_FS;
retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
&bmap);
if (retval) {
fputs(_("Failed to allocate block bitmap when "
"increasing inode size\n"), stderr);
return retval;
}
retval = get_move_bitmaps(fs, new_ino_blks_per_grp, bmap);
if (retval) {
fputs(_("Not enough space to increase inode size \n"), stderr);
goto err_out;
}
retval = move_block(fs, bmap);
if (retval) {
fputs(_("Failed to relocate blocks during inode resize \n"),
stderr);
goto err_out;
}
retval = inode_scan_and_fix(fs, bmap);
if (retval)
goto err_out_undo;
retval = group_desc_scan_and_fix(fs, bmap);
if (retval)
goto err_out_undo;
retval = expand_inode_table(fs, new_size);
if (retval)
goto err_out_undo;
ext2fs_calculate_summary_stats(fs);
fs->super->s_state |= EXT2_VALID_FS;
/* mark super block and block bitmap as dirty */
ext2fs_mark_super_dirty(fs);
ext2fs_mark_bb_dirty(fs);
err_out:
free_blk_move_list();
ext2fs_free_block_bitmap(bmap);
return retval;
err_out_undo:
free_blk_move_list();
ext2fs_free_block_bitmap(bmap);
fputs(_("Error in resizing the inode size.\n"
"Run e2undo to undo the "
"file system changes. \n"), stderr);
return retval;
}
static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr)
{
errcode_t retval = 0;
const char *tdb_dir;
char *tdb_file = NULL;
char *dev_name, *tmp_name;
/* (re)open a specific undo file */
if (undo_file && undo_file[0] != 0) {
retval = set_undo_io_backing_manager(*io_ptr);
if (retval)
goto err;
*io_ptr = undo_io_manager;
retval = set_undo_io_backup_file(undo_file);
if (retval)
goto err;
printf(_("Overwriting existing filesystem; this can be undone "
"using the command:\n"
" e2undo %s %s\n\n"),
undo_file, name);
return retval;
}
/*
* Configuration via a conf file would be
* nice
*/
tdb_dir = getenv("E2FSPROGS_UNDO_DIR");
if (!tdb_dir)
tdb_dir = "/var/lib/e2fsprogs";
if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) ||
access(tdb_dir, W_OK))
return 0;
tmp_name = strdup(name);
if (!tmp_name)
goto errout;
dev_name = basename(tmp_name);
tdb_file = malloc(strlen(tdb_dir) + 9 + strlen(dev_name) + 7 + 1);
if (!tdb_file) {
free(tmp_name);
goto errout;
}
sprintf(tdb_file, "%s/tune2fs-%s.e2undo", tdb_dir, dev_name);
free(tmp_name);
if ((unlink(tdb_file) < 0) && (errno != ENOENT)) {
retval = errno;
com_err(program_name, retval,
_("while trying to delete %s"), tdb_file);
goto errout;
}
retval = set_undo_io_backing_manager(*io_ptr);
if (retval)
goto errout;
*io_ptr = undo_io_manager;
retval = set_undo_io_backup_file(tdb_file);
if (retval)
goto errout;
printf(_("Overwriting existing filesystem; this can be undone "
"using the command:\n"
" e2undo %s %s\n\n"),
tdb_file, name);
free(tdb_file);
return 0;
errout:
free(tdb_file);
err:
com_err("tune2fs", retval, "while trying to setup undo file\n");
return retval;
}
static 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] __attribute__ ((aligned(8)));
if (!ext2fs_has_feature_journal(sb) || 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;
}
#ifndef BUILD_AS_LIB
int main(int argc, char **argv)
#else
int tune2fs_main(int argc, char **argv)
#endif /* BUILD_AS_LIB */
{
errcode_t retval;
ext2_filsys fs;
struct ext2_super_block *sb;
io_manager io_ptr, io_ptr_orig = NULL;
int rc = 0;
char default_undo_file[1] = { 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;
add_error_table(&et_ext2_error_table);
#ifdef CONFIG_BUILD_FINDFS
if (strcmp(get_progname(argv[0]), "findfs") == 0)
do_findfs(argc, argv);
#endif
if (strcmp(get_progname(argv[0]), "e2label") == 0)
parse_e2label_options(argc, argv);
else
parse_tune2fs_options(argc, argv);
#ifdef CONFIG_TESTIO_DEBUG
if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_DEBUG")) {
io_ptr = test_io_manager;
test_io_backing_manager = unix_io_manager;
} else
#endif
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 | EXT2_FLAG_JOURNAL_DEV_OK;
/* 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"),
device_name);
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_BAD_MAGIC)
check_plausibility(device_name, CHECK_FS_EXIST, NULL);
else if (retval != EXT2_ET_MMP_FAILED)
fprintf(stderr, "%s",
_("Couldn't find valid filesystem superblock.\n"));
ext2fs_free(fs);
1997-04-26 21:21:57 +08:00
exit(1);
}
if (ext2fs_has_feature_journal_dev(fs->super)) {
fprintf(stderr, "%s", _("Cannot modify a journal device.\n"));
ext2fs_free(fs);
exit(1);
}
fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
if (I_flag) {
/*
* Check the inode size is right so we can issue an
* error message and bail before setting up the tdb
* file.
*/
if (new_inode_size == EXT2_INODE_SIZE(fs->super)) {
fprintf(stderr, _("The inode size is already %lu\n"),
new_inode_size);
rc = 1;
goto closefs;
}
if (new_inode_size < EXT2_INODE_SIZE(fs->super)) {
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;
}
check_fsck_needed(fs,
_("Resizing inodes could take some time."));
/*
* If inode resize is requested use the
* Undo I/O manager
*/
undo_file = default_undo_file;
}
/* Set up an undo file */
if (undo_file && io_ptr_orig == NULL) {
io_ptr_orig = io_ptr;
retval = tune2fs_setup_tdb(device_name, &io_ptr);
if (retval) {
rc = 1;
goto closefs;
}
if (io_ptr != io_ptr_orig) {
ext2fs_close_free(&fs);
goto retry_open;
}
}
sb = fs->super;
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
if (print_label) {
/* For e2label emulation */
printf("%.*s\n", (int) sizeof(sb->s_volume_name),
sb->s_volume_name);
remove_error_table(&et_ext2_error_table);
goto closefs;
}
retval = ext2fs_check_if_mounted(device_name, &mount_flags);
if (retval) {
com_err("ext2fs_check_if_mount", retval,
_("while determining whether %s is mounted."),
device_name);
rc = 1;
goto closefs;
}
/* Normally we only need to write out the superblock */
fs->flags |= EXT2_FLAG_SUPER_ONLY;
1997-04-26 21:21:57 +08:00
1997-04-29 22:53:37 +08:00
if (c_flag) {
sb->s_max_mnt_count = max_mount_count;
1997-04-26 21:21:57 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting maximal mount count to %d\n"),
max_mount_count);
1997-04-26 21:21:57 +08:00
}
1997-04-29 22:53:37 +08:00
if (C_flag) {
sb->s_mnt_count = mount_count;
1997-04-29 22:53:37 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting current mount count to %d\n"), mount_count);
1997-04-29 22:53:37 +08:00
}
if (e_flag) {
sb->s_errors = errors;
1997-04-26 21:21:57 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting error behavior to %d\n"), errors);
1997-04-26 21:21:57 +08:00
}
if (g_flag) {
sb->s_def_resgid = resgid;
1997-04-26 21:34:30 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting reserved blocks gid to %lu\n"), resgid);
1997-04-26 21:34:30 +08:00
}
if (i_flag) {
if ((unsigned long long)interval >= (1ULL << 32)) {
com_err(program_name, 0,
_("interval between checks is too big (%lu)"),
interval);
rc = 1;
goto closefs;
}
sb->s_checkinterval = interval;
1997-04-26 21:21:57 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting interval between checks to %lu seconds\n"),
interval);
1997-04-26 21:21:57 +08:00
}
if (m_flag) {
ext2fs_r_blocks_count_set(sb, reserved_ratio *
ext2fs_blocks_count(sb) / 100.0);
1997-04-26 21:21:57 +08:00
ext2fs_mark_super_dirty(fs);
printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"),
reserved_ratio, ext2fs_r_blocks_count(sb));
1997-04-26 21:21:57 +08:00
}
if (r_flag) {
if (reserved_blocks > ext2fs_blocks_count(sb)/2) {
com_err(program_name, 0,
_("reserved blocks count is too big (%llu)"),
reserved_blocks);
rc = 1;
goto closefs;
1997-04-26 21:34:30 +08:00
}
ext2fs_r_blocks_count_set(sb, reserved_blocks);
1997-04-26 21:34:30 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting reserved blocks count to %llu\n"),
reserved_blocks);
1997-04-26 21:34:30 +08:00
}
1997-04-30 01:48:10 +08:00
if (s_flag == 1) {
if (ext2fs_has_feature_sparse_super(sb)) {
2003-12-07 14:28:50 +08:00
fputs(_("\nThe filesystem already has sparse "
"superblocks.\n"), stderr);
} else if (ext2fs_has_feature_meta_bg(sb)) {
e2fsprogs: Disallow tune2fs enabling sparse_super with ext4 meta_bg enabled When meta_bg feature is enabled, group descriptor block is allocated every 128 block group (or every 64 block group if 64bit feature is enabled). In such situation, files in block group more than #128 will be removed if sparse_super feature is enabled with tune2fs and afterwards necessary e2fsck running. Because tune2fs does not reallocate group descriptor blocks but just set sparse_super feature. If ext4 has sparse_super, ext2fs_descriptor_block_loc2() called by e2fsck thinks the block group (e.g. #128) that it has group descriptor block at the head offset. But that offset is used as backup super block before. So e2fsck fixes ext4 based on invalid group descriptor blocks and this cause data lost. The patch avoids this problem simply by disallow tune2fs enabling sparse_super if meta_bg is enabled. Steps to reproduce: 1. Create ext4 which has meta_bg, ^sparse_super and 129+ block groups. # mke2fs -t ext4 -O meta_bg,^resize_inode,^sparse_super DEV 17G # mount DEV /MP 2. Create direcotry and files which use block group #128's metadata. # echo $((8192*128+1)) > /sys/fs/ext4/DEV/inode_goal # mkdir /MP/DIR # for i in $(seq 1 100); do dd if=/dev/urandom of=/MP/DIR/file$i bs=1024 count=10; done 3. Enable sparse_super with tune2fs then execute e2fsck. Data in block group #128 will be lost!! # umount DEV # tune2fs -O sparse_super DEV # e2fsck/e2fsck -yf DEV Signed-off-by: Akira Fujita <a-fujita@rs.jp.ne.cocm> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-02-07 04:11:52 +08:00
fputs(_("\nSetting the sparse superblock flag not "
"supported\nfor filesystems with "
"the meta_bg feature enabled.\n"),
stderr);
rc = 1;
goto closefs;
} else {
ext2fs_set_feature_sparse_super(sb);
sb->s_state &= ~EXT2_VALID_FS;
1997-04-30 01:48:10 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("\nSparse superblock flag set. %s"),
_(please_fsck));
1997-04-30 01:48:10 +08:00
}
}
if (s_flag == 0) {
e2fsprogs: Disallow tune2fs enabling sparse_super with ext4 meta_bg enabled When meta_bg feature is enabled, group descriptor block is allocated every 128 block group (or every 64 block group if 64bit feature is enabled). In such situation, files in block group more than #128 will be removed if sparse_super feature is enabled with tune2fs and afterwards necessary e2fsck running. Because tune2fs does not reallocate group descriptor blocks but just set sparse_super feature. If ext4 has sparse_super, ext2fs_descriptor_block_loc2() called by e2fsck thinks the block group (e.g. #128) that it has group descriptor block at the head offset. But that offset is used as backup super block before. So e2fsck fixes ext4 based on invalid group descriptor blocks and this cause data lost. The patch avoids this problem simply by disallow tune2fs enabling sparse_super if meta_bg is enabled. Steps to reproduce: 1. Create ext4 which has meta_bg, ^sparse_super and 129+ block groups. # mke2fs -t ext4 -O meta_bg,^resize_inode,^sparse_super DEV 17G # mount DEV /MP 2. Create direcotry and files which use block group #128's metadata. # echo $((8192*128+1)) > /sys/fs/ext4/DEV/inode_goal # mkdir /MP/DIR # for i in $(seq 1 100); do dd if=/dev/urandom of=/MP/DIR/file$i bs=1024 count=10; done 3. Enable sparse_super with tune2fs then execute e2fsck. Data in block group #128 will be lost!! # umount DEV # tune2fs -O sparse_super DEV # e2fsck/e2fsck -yf DEV Signed-off-by: Akira Fujita <a-fujita@rs.jp.ne.cocm> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-02-07 04:11:52 +08:00
fputs(_("\nClearing the sparse superblock flag not supported.\n"),
stderr);
rc = 1;
goto closefs;
1997-04-30 01:48:10 +08:00
}
if (T_flag) {
sb->s_lastcheck = last_check_time;
ext2fs_mark_super_dirty(fs);
printf(_("Setting time filesystem last checked to %s\n"),
ctime(&last_check_time));
}
if (u_flag) {
sb->s_def_resuid = resuid;
1997-04-26 21:34:30 +08:00
ext2fs_mark_super_dirty(fs);
printf(_("Setting reserved blocks uid to %lu\n"), resuid);
1997-04-26 21:34:30 +08:00
}
1997-04-29 22:53:37 +08:00
if (L_flag) {
if (strlen(new_label) > sizeof(sb->s_volume_name))
fputs(_("Warning: label too long, truncating.\n"),
2003-12-07 14:28:50 +08:00
stderr);
1997-04-29 22:53:37 +08:00
memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
strncpy(sb->s_volume_name, new_label,
sizeof(sb->s_volume_name));
ext2fs_mark_super_dirty(fs);
}
if (M_flag) {
memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
strncpy(sb->s_last_mounted, new_last_mounted,
sizeof(sb->s_last_mounted));
ext2fs_mark_super_dirty(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);
rc = 1;
goto closefs;
}
handle_quota_options(fs);
}
1997-04-29 22:53:37 +08:00
if (U_flag) {
int set_csum = 0;
dgrp_t i;
char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
__u8 old_uuid[UUID_SIZE];
if (ext2fs_has_group_desc_csum(fs)) {
/*
* Changing the UUID on a metadata_csum FS requires
* rewriting all metadata, which can race with a
* mounted fs. Don't allow that unless we're saving
* the checksum seed.
*/
if ((mount_flags & EXT2_MF_MOUNTED) &&
!ext2fs_has_feature_csum_seed(fs->super) &&
ext2fs_has_feature_metadata_csum(fs->super)) {
fputs(_("The UUID may only be "
"changed when the filesystem is "
"unmounted.\n"), stderr);
fputs(_("If you only use kernels newer than "
"v4.4, run 'tune2fs -O "
"metadata_csum_seed' and re-run this "
"command.\n"), stderr);
try_confirm_csum_seed_support();
exit(1);
}
if (!ext2fs_has_feature_csum_seed(fs->super))
check_fsck_needed(fs,
_("Setting UUID on a checksummed "
"filesystem could take some time."));
/*
* Determine if the block group checksums are
* correct so we know whether or not to set
* them later on.
*/
for (i = 0; i < fs->group_desc_count; i++)
if (!ext2fs_group_desc_csum_verify(fs, i))
break;
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)) {
1997-04-29 22:53:37 +08:00
uuid_clear(sb->s_uuid);
} else if (strcasecmp(new_UUID, "time") == 0) {
uuid_generate_time(sb->s_uuid);
1997-04-29 22:53:37 +08:00
} 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, "%s",
_("Invalid UUID format\n"));
rc = 1;
goto closefs;
1997-04-29 22:53:37 +08:00
}
ext2fs_init_csum_seed(fs);
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;
}
1997-04-29 22:53:37 +08:00
ext2fs_mark_super_dirty(fs);
if (ext2fs_has_feature_metadata_csum(fs->super) &&
!ext2fs_has_feature_csum_seed(fs->super))
rewrite_checksums = 1;
1997-04-29 22:53:37 +08:00
}
if (I_flag) {
if (mount_flags & EXT2_MF_MOUNTED) {
fputs(_("The inode size may only be "
"changed when the filesystem is "
"unmounted.\n"), stderr);
rc = 1;
goto closefs;
}
if (ext2fs_has_feature_flex_bg(fs->super)) {
fputs(_("Changing the inode size not supported for "
"filesystems with the flex_bg\n"
"feature enabled.\n"),
stderr);
rc = 1;
goto closefs;
}
/*
* We want to update group descriptor also
* with the new free inode count
*/
if (rewrite_checksums)
fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
retval = resize_inode(fs, new_inode_size);
if (rewrite_checksums)
fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval == 0) {
printf(_("Setting inode size %lu\n"),
new_inode_size);
rewrite_checksums = 1;
} else {
printf("%s", _("Failed to change inode size\n"));
rc = 1;
goto closefs;
}
}
1997-04-29 22:53:37 +08:00
if (rewrite_checksums)
rewrite_metadata_checksums(fs);
1997-04-26 21:21:57 +08:00
if (l_flag)
list_super(sb);
if (stride_set) {
sb->s_raid_stride = stride;
ext2fs_mark_super_dirty(fs);
printf(_("Setting stride size to %d\n"), stride);
}
if (stripe_width_set) {
sb->s_raid_stripe_width = stripe_width;
ext2fs_mark_super_dirty(fs);
printf(_("Setting stripe width to %d\n"), stripe_width);
}
if (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);
printf(_("Setting extended default mount options to '%s'\n"),
ext_mount_opts);
free(ext_mount_opts);
}
#ifdef NO_RECOVERY
/* 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: The journal is dirty. You may wish to replay the journal like:\n\n"
"\te2fsck -E journal_only %s\n\n"
"then rerun this command. Otherwise, any changes made may be overwritten\n"
"by journal recovery.\n"), device_name);
}
#else
/* Recover the journal if possible. */
if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) &&
ext2fs_has_feature_journal_needs_recovery(fs->super)) {
errcode_t err;
printf(_("Recovering journal.\n"));
err = ext2fs_run_ext3_journal(&fs);
if (err) {
com_err("tune2fs", err, "while recovering journal.\n");
printf(_("Please run e2fsck -fy %s.\n"), argv[1]);
goto closefs;
}
ext2fs_clear_feature_journal_needs_recovery(fs->super);
ext2fs_mark_super_dirty(fs);
}
#endif
free(device_name);
remove_error_table(&et_ext2_error_table);
closefs:
if (rc) {
ext2fs_mmp_stop(fs);
#ifndef BUILD_AS_LIB
exit(1);
#endif
}
convert_64bit(fs, feature_64bit);
return (ext2fs_close_free(&fs) ? 1 : 0);
1997-04-26 21:21:57 +08:00
}