2015-08-07 10:51:42

by Jan Kara

[permalink] [raw]
Subject: [PATCH 00/19] e2fsprogs: Resizing rewrite

Hello,

this patch series factors out large parts of resizing code into libext2fs.
The motivation of this is that handling of enabling / disabling of more and
more features requires moving blocks or inodes with rewriting all the
references and using resize2fs for that is not logical from user interface
POV.

The series is structured as follows.

* Patches 1-7 are various small cleanups and improvements.

* Patches 8-9 implement the main functionality. The functionality is
implemented by two functions:

ext2fs_move_blocks() which gets filesystem and bitmap of blocks which it should
make free and the function takes care of everything needed to make the blocks
free.

ext2fs_move_inodes() which gets filesystem and bitmap of inodes which it should
make free and the function takes care of everything needed to make these inodes
free.

* Patch 10 implements enabling / disabling 64-bit feature in tune2fs where it
is more logical

* Patches 11-13 add support for changing number of reserved inodes in e2fsprogs

* Patches 14-19 change resize2fs itself to use code from libext2fs to perform
resizing.

I have tested the new code for various combinations of fs features to make sure
I didn't mess up anything but given this is effectively a rewrite of filesystem
resizing, please handle with care.

Honza


2015-08-07 10:51:42

by Jan Kara

[permalink] [raw]
Subject: [PATCH 02/19] ext2fs: Use range marking function to mark all inode table blocks as used

From: Jan Kara <[email protected]>

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/gen_bitmap64.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index 34122cfa8901..2a54514b6b1e 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -913,7 +913,6 @@ void ext2fs_init_uninit_block_bitmaps(ext2_filsys fs)
{
blk64_t blk, lblk;
dgrp_t g;
- int i;

if (!ext2fs_has_group_desc_csum(fs))
return;
@@ -932,9 +931,8 @@ void ext2fs_init_uninit_block_bitmaps(ext2_filsys fs)
ext2fs_block_bitmap_loc(fs, g));
ext2fs_mark_block_bitmap2(fs->block_map,
ext2fs_inode_bitmap_loc(fs, g));
- for (i = 0, blk = ext2fs_inode_table_loc(fs, g);
- i < (unsigned int) fs->inode_blocks_per_group;
- i++, blk++)
- ext2fs_mark_block_bitmap2(fs->block_map, blk);
+ ext2fs_mark_block_bitmap_range2(fs->block_map,
+ ext2fs_inode_table_loc(fs, g),
+ fs->inode_blocks_per_group);
}
}
--
2.1.4


2015-08-07 10:51:47

by Jan Kara

[permalink] [raw]
Subject: [PATCH 13/19] tune2fs: Add support for changing number of reserved inodes

Signed-off-by: Jan Kara <[email protected]>
---
misc/tune2fs.8.in | 5 +++
misc/tune2fs.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)

diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
index 4373fc49b255..922705329112 100644
--- a/misc/tune2fs.8.in
+++ b/misc/tune2fs.8.in
@@ -236,6 +236,11 @@ program.
This superblock setting is only honored in 2.6.35+ kernels;
and not at all by the ext2 and ext3 file system drivers.
.TP
+.BI reserved_inodes= number_of_reserved_inodes
+Set the number of inodes reserved for system files. This number must be
+at least 10. Currently 10 is enough but future features may require additional
+reserved inodes. Reserving more inodes requires full file system scan so it
+can take a long time.
.B test_fs
Set a flag in the filesystem superblock indicating that it may be
mounted using experimental kernel code, such as the ext4dev filesystem.
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index d6bfad3374d1..d6e75fe00814 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -92,6 +92,7 @@ static char *features_cmd;
static char *mntopts_cmd;
static int stride, stripe_width;
static int stride_set, stripe_width_set;
+static unsigned long new_first_ino;
static char *extended_cmd;
static unsigned long new_inode_size;
static char *ext_mount_opts;
@@ -2298,6 +2299,34 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
continue;
}
ext_mount_opts = strdup(arg);
+ } else if (!strcmp(token, "reserved_inodes")) {
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+ new_first_ino = strtoul(arg, &p, 0);
+ if (*p) {
+ fprintf(stderr,
+ _("Invalid number of reserved inodes "
+ "%s\n"),
+ arg);
+ r_usage++;
+ continue;
+ }
+ /* Ino 0 is invalid so bump by 1... */
+ new_first_ino++;
+ if (new_first_ino < EXT2_GOOD_OLD_FIRST_INO) {
+ fprintf(stderr,
+ _("Too few reserved inodes "
+ "%s (must be at least %u)\n"),
+ arg, EXT2_GOOD_OLD_FIRST_INO - 1);
+ r_usage++;
+ continue;
+ }
+ /*
+ * Here should go further feature tests to disallow
+ * admin to free used system inode
+ */
} else
r_usage++;
}
@@ -2312,6 +2341,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
"\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"
+ "\treserved_inodes=<number of reserved inodes>\n"
"\ttest_fs\n"
"\t^test_fs\n"));
free(buf);
@@ -2986,6 +3016,75 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE])
return 0;
}

+/* Zero range of inodes and mark them as free / used */
+static errcode_t zero_inodes_range(ext2_filsys fs, ext2_ino_t start,
+ ext2_ino_t end, int inuse)
+{
+ char *inode;
+ int length = EXT2_INODE_SIZE(fs->super);
+ ext2_ino_t ino;
+ errcode_t retval;
+
+ retval = ext2fs_get_memzero(length, &inode);
+ if (retval)
+ return retval;
+
+ for (ino = start; ino <= end; ino++) {
+ ext2fs_inode_alloc_stats(fs, ino, inuse);
+ retval = ext2fs_write_inode_full(fs, ino,
+ (struct ext2_inode *)inode,
+ length);
+ if (retval)
+ break;
+ }
+ ext2fs_free_mem(inode);
+
+ return retval;
+}
+
+static errcode_t update_reserved_inodes(ext2_filsys fs)
+{
+ errcode_t retval = 0;
+ ext2fs_inode_bitmap imap;
+ ext2_ino_t ino, first_ino = fs->super->s_first_ino;
+
+ if (new_first_ino == first_ino)
+ return 0;
+
+ /* Group descriptors will need writing as well */
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+
+ /* Freeing reserved inodes is easy */
+ if (new_first_ino < first_ino) {
+ retval = zero_inodes_range(fs, new_first_ino, first_ino - 1,
+ -1);
+ if (retval)
+ return retval;
+ goto out;
+ }
+
+ retval = ext2fs_allocate_inode_bitmap(fs, "inodes to move", &imap);
+ if (retval)
+ return retval;
+
+ for (ino = fs->super->s_first_ino; ino < new_first_ino; ino++)
+ ext2fs_mark_inode_bitmap2(imap, ino);
+
+ retval = ext2fs_move_inodes(fs, imap);
+ ext2fs_free_inode_bitmap(imap);
+ if (retval)
+ return retval;
+
+ retval = zero_inodes_range(fs, first_ino, new_first_ino - 1, +1);
+ if (retval)
+ return retval;
+out:
+ fs->super->s_first_ino = new_first_ino;
+ ext2fs_mark_super_dirty(fs);
+
+ return 0;
+}
+
int main(int argc, char **argv)
{
errcode_t retval;
@@ -3404,6 +3503,9 @@ retry_open:
if (feature_64bit)
convert_64bit(&fs, feature_64bit);

+ if (new_first_ino)
+ update_reserved_inodes(fs);
+
if (rewrite_checksums)
rewrite_metadata_checksums(fs);

--
2.1.4


2015-08-07 10:51:46

by Jan Kara

[permalink] [raw]
Subject: [PATCH 11/19] mke2fs: Allow specifying number of reserved inodes

From: Jan Kara <[email protected]>

Add option to specify number of reserved inodes in the created
filesystem.

Signed-off-by: Jan Kara <[email protected]>
---
misc/mke2fs.8.in | 6 ++++++
misc/mke2fs.c | 40 +++++++++++++++++++++++++++++++++++++++-
misc/mke2fs.conf.5.in | 6 ++++++
3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
index 40c40d3ed065..520a07185f9f 100644
--- a/misc/mke2fs.8.in
+++ b/misc/mke2fs.8.in
@@ -384,6 +384,12 @@ Do not attempt to discard blocks at mkfs time.
@[email protected] quota
@QUOTA_MAN_COMMENT@feature is set. Without this extended option, the default
@QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
+.TP
+.BI reserved_inodes= number
+Specify the number of inodes reserved for system files. This number must be
+at least 10. Currently 10 is enough but future features may require additional
+reserved inodes. Reserving more inodes after file system is created requires
+full file system scan so it can take a long time.
.RE
.TP
.BI \-f " fragment-size"
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 78b1252d8519..d61d1a332e67 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1024,6 +1024,34 @@ static void parse_extended_opts(struct ext2_super_block *param,
r_usage++;
continue;
}
+ } else if (!strcmp(token, "reserved_inodes")) {
+ unsigned int reserved_inodes;
+
+ if (!arg) {
+ r_usage++;
+ badopt = token;
+ continue;
+ }
+ reserved_inodes = strtoul(arg, &p, 0);
+ if (*p) {
+ fprintf(stderr,
+ _("Invalid number of reserved inodes "
+ "%s\n"),
+ arg);
+ r_usage++;
+ continue;
+ }
+ /* Ino 0 is invalid so bump by 1... */
+ reserved_inodes++;
+ if (reserved_inodes < EXT2_GOOD_OLD_FIRST_INO) {
+ fprintf(stderr,
+ _("Too few reserved inodes "
+ "%s (must be at least %u)\n"),
+ arg, EXT2_GOOD_OLD_FIRST_INO - 1);
+ r_usage++;
+ continue;
+ }
+ param->s_first_ino = reserved_inodes;
} else {
r_usage++;
badopt = token;
@@ -1049,7 +1077,8 @@ static void parse_extended_opts(struct ext2_super_block *param,
"\ttest_fs\n"
"\tdiscard\n"
"\tnodiscard\n"
- "\tquotatype=<usr OR grp>\n\n"),
+ "\tquotatype=<usr OR grp>\n"
+ "\treserved_inodes=<number of reserved inodes>\n\n"),
badopt ? badopt : "");
free(buf);
exit(1);
@@ -2422,6 +2451,15 @@ profile_error:
exit(1);
}

+ /* Count with one more inode for lost+found */
+ if (fs_param.s_first_ino >= fs_param.s_inodes_count + 1) {
+ com_err(program_name, 0, _("asked for more reserved inodes than filesystem has "
+ "available (%u >= %u)\n"),
+ (unsigned int)fs_param.s_first_ino,
+ (unsigned int)fs_param.s_inodes_count + 1);
+ exit(1);
+ }
+
/*
* Calculate number of blocks to reserve
*/
diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
index ad6c11b3cb7b..06ca9e4eabc4 100644
--- a/misc/mke2fs.conf.5.in
+++ b/misc/mke2fs.conf.5.in
@@ -195,6 +195,12 @@ specify one on the command line, and the filesystem-type
specific section of the configuration file does not specify a default
reserved ratio. This value can be a floating point number.
.TP
+.I reserved_inodes
+This relation specifies the default number of inodes reserved for system files.
+The number must be at least 10. Currently 10 is enough but future features may
+require additional reserved inodes. Reserving more inodes after file system is
+created requires full file system scan so it can take a long time.
+.TP
.I undo_dir
This relation specifies the directory where the undo file should be
stored. It can be overridden via the
--
2.1.4


2015-08-07 10:51:46

by Jan Kara

[permalink] [raw]
Subject: [PATCH 10/19] tune2fs: Implement setting and disabling of 64-bit feature

Currently resize2fs is used to toggle 64-bit feature. However tune2fs
should logically handle that as toggling any other feature. Since we
have now factored out block moving code from resize2fs it is relatively
easy to implement toggling of the feature within tune2fs.

For now we leave the same functionality also in resize2fs for testing.
Later we can make resize2fs call tune2fs for backward compatibility.

Signed-off-by: Jan Kara <[email protected]>
---
misc/tune2fs.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 345 insertions(+), 22 deletions(-)

diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index d2e8b20f64e2..d6bfad3374d1 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -447,10 +447,327 @@ static void request_fsck_afterwards(ext2_filsys fs)
printf("%s", _("(and reboot afterwards!)\n"));
}

-static void convert_64bit(ext2_filsys fs, int direction)
+static errcode_t fix_resize_inode(ext2_filsys fs)
{
- if (!direction)
- return;
+ errcode_t retval;
+
+ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE))
+ return 0;
+
+ retval = ext2fs_wipe_resize_inode(fs);
+ if (retval)
+ return retval;
+
+ return ext2fs_create_resize_inode(fs);
+}
+
+/*
+ * Make blocks that are needed for new group metadata unused and write there
+ * new group descriptors
+ */
+static errcode_t move_bg_metadata(ext2_filsys fs, ext2_filsys new_fs)
+{
+ dgrp_t i;
+ blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
+ ext2fs_block_bitmap old_map = NULL, new_map = NULL;
+ int old, new;
+ errcode_t retval;
+ int cluster_ratio;
+ int need_new_block = 0;
+
+ retval = ext2fs_allocate_block_bitmap(fs, "old map", &old_map);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_allocate_block_bitmap(fs, "new map", &new_map);
+ if (retval)
+ goto out;
+
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_INCOMPAT_META_BG)) {
+ old_desc_blocks = fs->super->s_first_meta_bg;
+ new_desc_blocks = new_fs->super->s_first_meta_bg;
+ } else {
+ old_desc_blocks = fs->desc_blocks +
+ fs->super->s_reserved_gdt_blocks;
+ new_desc_blocks = new_fs->desc_blocks +
+ new_fs->super->s_reserved_gdt_blocks;
+ }
+
+ /* Construct bitmaps of super/descriptor blocks in old and new fs */
+ for (i = 0; i < fs->group_desc_count; i++) {
+ retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
+ if (retval)
+ goto out;
+ if (b)
+ ext2fs_mark_block_bitmap2(old_map, b);
+ for (j = 0; c != 0 && j < old_desc_blocks; j++)
+ ext2fs_mark_block_bitmap2(old_map, c + j);
+ if (d)
+ ext2fs_mark_block_bitmap2(old_map, d);
+
+ retval = ext2fs_super_and_bgd_loc2(new_fs, i, &b, &c, &d, NULL);
+ if (retval)
+ goto out;
+ if (b)
+ ext2fs_mark_block_bitmap2(new_map, b);
+ for (j = 0; c != 0 && j < new_desc_blocks; j++)
+ ext2fs_mark_block_bitmap2(new_map, c + j);
+ if (d)
+ ext2fs_mark_block_bitmap2(new_map, d);
+ }
+
+ cluster_ratio = EXT2FS_CLUSTER_RATIO(new_fs);
+
+ /* Find changes in block allocations for bg metadata */
+ for (b = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ b < ext2fs_blocks_count(new_fs->super);
+ b += cluster_ratio) {
+ old = ext2fs_test_block_bitmap2(old_map, b);
+ new = ext2fs_test_block_bitmap2(new_map, b);
+
+ if (old && !new) {
+ /* mark old_map, unmark new_map */
+ if (cluster_ratio == 1)
+ ext2fs_block_alloc_stats2(new_fs, b, -1);
+ } else if (!old && new)
+ need_new_block = 1; /* unmark old_map, mark new_map */
+ else {
+ ext2fs_unmark_block_bitmap2(old_map, b);
+ ext2fs_unmark_block_bitmap2(new_map, b);
+ }
+ }
+
+ /*
+ * new_map now shows blocks that have been newly allocated.
+ * old_map now shows blocks that have been newly freed.
+ */
+
+ if (need_new_block) {
+ /*
+ * Now relocate metadata & data conflicting with new descriptor
+ * blocks
+ */
+ retval = ext2fs_move_blocks(new_fs, new_map, old_map);
+ if (retval)
+ goto out;
+
+ /* Now mark blocks for new group descriptors as used */
+ for (b = 0; b < ext2fs_blocks_count(new_fs->super);
+ b += cluster_ratio) {
+ if (ext2fs_test_block_bitmap2(new_map, b) &&
+ !ext2fs_test_block_bitmap2(new_fs->block_map, b))
+ ext2fs_block_alloc_stats2(new_fs, b, +1);
+ }
+ }
+
+ if (cluster_ratio > 1) {
+ /* Free unused clusters */
+ for (b = 0; b < ext2fs_blocks_count(new_fs->super);
+ b += cluster_ratio)
+ if (ext2fs_test_block_bitmap2(old_map, b))
+ ext2fs_block_alloc_stats2(new_fs, b, -1);
+ }
+
+ /* Now update resize inode */
+ retval = fix_resize_inode(new_fs);
+out:
+ if (old_map)
+ ext2fs_free_block_bitmap(old_map);
+ if (new_map)
+ ext2fs_free_block_bitmap(new_map);
+ return retval;
+}
+
+
+/* Toggle 64bit mode */
+static errcode_t resize_group_descriptors(ext2_filsys *fs_p, int direction)
+{
+ void *o, *n, *new_group_desc;
+ dgrp_t i;
+ int copy_size;
+ errcode_t retval;
+ ext2_filsys new_fs = NULL;
+ ext2_filsys fs = *fs_p;
+
+ retval = ext2fs_dup_handle(fs, &new_fs);
+ if (retval)
+ return retval;
+
+ if (direction < 0) {
+ new_fs->super->s_feature_incompat &=
+ ~EXT4_FEATURE_INCOMPAT_64BIT;
+ new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
+ } else {
+ new_fs->super->s_feature_incompat |=
+ EXT4_FEATURE_INCOMPAT_64BIT;
+ new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
+ }
+
+ o = new_fs->group_desc;
+ new_fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
+ EXT2_DESC_PER_BLOCK(new_fs->super));
+ retval = ext2fs_get_arrayzero(new_fs->desc_blocks,
+ fs->blocksize, &new_group_desc);
+ if (retval)
+ goto out;
+
+ n = new_group_desc;
+
+ if (EXT2_DESC_SIZE(fs->super) <= EXT2_DESC_SIZE(new_fs->super))
+ copy_size = EXT2_DESC_SIZE(fs->super);
+ else
+ copy_size = EXT2_DESC_SIZE(new_fs->super);
+ for (i = 0; i < fs->group_desc_count; i++) {
+ memcpy(n, o, copy_size);
+ n += EXT2_DESC_SIZE(new_fs->super);
+ o += EXT2_DESC_SIZE(fs->super);
+ }
+
+ ext2fs_free_mem(&new_fs->group_desc);
+ new_fs->group_desc = new_group_desc;
+
+ for (i = 0; i < fs->group_desc_count; i++)
+ ext2fs_group_desc_csum_set(new_fs, i);
+
+ /*
+ * Update number of reserved blocks so that we need to alloc / free as
+ * few blocks as possible
+ */
+ if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
+ (fs->desc_blocks != new_fs->desc_blocks)) {
+ int new;
+
+ new = ((int) new_fs->super->s_reserved_gdt_blocks) +
+ (fs->desc_blocks - new_fs->desc_blocks);
+ if (new < 0)
+ new = 0;
+ if (new > (int) fs->blocksize / 4)
+ new = fs->blocksize / 4;
+ new_fs->super->s_reserved_gdt_blocks = new;
+ }
+
+ retval = move_bg_metadata(fs, new_fs);
+
+ /* Return the modified filesystem */
+ new_fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ ext2fs_mark_super_dirty(new_fs);
+ ext2fs_free(fs);
+ *fs_p = new_fs;
+ new_fs = NULL;
+out:
+ if (new_fs)
+ ext2fs_free(new_fs);
+ return retval;
+}
+
+/* Zero out the high bits of extent fields */
+static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ ext2_extent_handle_t handle;
+ struct ext2fs_extent extent;
+ int op = EXT2_EXTENT_ROOT;
+ errcode_t errcode;
+
+ if (!(inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+
+ errcode = ext2fs_extent_open(fs, ino, &handle);
+ if (errcode)
+ return errcode;
+
+ while (1) {
+ errcode = ext2fs_extent_get(handle, op, &extent);
+ if (errcode)
+ break;
+
+ op = EXT2_EXTENT_NEXT_SIB;
+
+ if (extent.e_pblk > (1ULL << 32)) {
+ extent.e_pblk &= (1ULL << 32) - 1;
+ errcode = ext2fs_extent_replace(handle, 0, &extent);
+ if (errcode)
+ break;
+ }
+ }
+
+ /* Ok if we run off the end */
+ if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+ errcode = 0;
+ ext2fs_extent_free(handle);
+ return errcode;
+}
+
+/* Zero out the high bits of inodes. */
+static errcode_t zero_high_bits_in_inodes(ext2_filsys fs)
+{
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode *inode = NULL;
+ ext2_inode_scan scan = NULL;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ if (fs->super->s_creator_os != EXT2_OS_LINUX)
+ return 0;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ return retval;
+
+ 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))
+ continue;
+
+ /*
+ * Here's how we deal with high block number fields:
+ *
+ * - i_size_high has been been written out with i_size_lo
+ * since the ext2 days, so no conversion is needed.
+ *
+ * - i_blocks_hi is guarded by both the huge_file feature and
+ * inode flags and has always been written out with
+ * i_blocks_lo if the feature is set. The field is only
+ * ever read if both feature and inode flag are set, so
+ * we don't need to zero it now.
+ *
+ * - i_file_acl_high can be uninitialized, so zero it if
+ * it isn't already.
+ */
+ if (inode->osd2.linux2.l_i_file_acl_high) {
+ inode->osd2.linux2.l_i_file_acl_high = 0;
+ retval = ext2fs_write_inode_full(fs, ino, inode,
+ length);
+ if (retval)
+ goto out;
+ }
+
+ retval = zero_high_bits_in_extents(fs, ino, inode);
+ if (retval)
+ goto out;
+ } while (ino);
+
+out:
+ if (inode)
+ ext2fs_free_mem(&inode);
+ if (scan)
+ ext2fs_close_inode_scan(scan);
+ return retval;
+}
+
+static errcode_t convert_64bit(ext2_filsys *fs_p, int direction)
+{
+ ext2_filsys fs = *fs_p;
+ errcode_t retval;

/*
* Is resize2fs going to demand a fsck run? Might as well tell the
@@ -459,21 +776,25 @@ static void convert_64bit(ext2_filsys fs, int direction)
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))
+ 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);
+ return 0;
+ }

- 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"));
+ /*
+ * Read bitmaps now since we are definitely going to need them and they
+ * get properly duplicated when creating new fs handle in
+ * resize_group_descriptors()
+ */
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval)
+ return retval;
+
+ retval = resize_group_descriptors(fs_p, direction);
+ if (retval)
+ return retval;
+
+ return zero_high_bits_in_inodes(*fs_p);
}

/* Rewrite extents */
@@ -1253,9 +1574,9 @@ mmp_error:
}

/*
- * 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.
+ * Handle 64-bit conversion. We return the feature to the original
+ * value for now until the conversion really takes place. Otherwise
+ * some helper macros get confused.
*/
if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_64BIT)) {
@@ -1264,8 +1585,8 @@ mmp_error:
"while mounted!\n"));
exit(1);
}
- sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
feature_64bit = 1;
+ sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
}
if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
EXT4_FEATURE_INCOMPAT_64BIT)) {
@@ -1274,8 +1595,8 @@ mmp_error:
"while mounted!\n"));
exit(1);
}
- sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
feature_64bit = -1;
+ sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
}

if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
@@ -3080,6 +3401,9 @@ retry_open:
}
}

+ if (feature_64bit)
+ convert_64bit(&fs, feature_64bit);
+
if (rewrite_checksums)
rewrite_metadata_checksums(fs);

@@ -3113,6 +3437,5 @@ closefs:
exit(1);
}

- convert_64bit(fs, feature_64bit);
return (ext2fs_close_free(&fs) ? 1 : 0);
}
--
2.1.4


2015-08-07 10:51:47

by Jan Kara

[permalink] [raw]
Subject: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

From: Jan Kara <[email protected]>

We ran out of reserved inodes so bump the default number of reserved
inodes to 64 to get some breathing space. Otherwise we have to do a full
fs scan when increasing number of reserved inodes when some feature
needing another reserved inode is enabled. This consumes 13.5 KB on a
filesystem which is negligible these days.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/initialize.c | 2 +-
misc/mke2fs.8.in | 8 ++++----
misc/mke2fs.conf.5.in | 7 ++++---
3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index 75fbf8ee0061..0ecf4606ce48 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,

set_field(s_rev_level, EXT2_GOOD_OLD_REV);
if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
- set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
+ set_field(s_first_ino, 64);
set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
int extra_isize = sizeof(struct ext2_inode_large) -
diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
index 520a07185f9f..6eef1d97bfbf 100644
--- a/misc/mke2fs.8.in
+++ b/misc/mke2fs.8.in
@@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
@QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
.TP
.BI reserved_inodes= number
-Specify the number of inodes reserved for system files. This number must be
-at least 10. Currently 10 is enough but future features may require additional
-reserved inodes. Reserving more inodes after file system is created requires
-full file system scan so it can take a long time.
+Specify the number of inodes reserved for system files. This number must be at
+least 10, default is 64. Currently 10 is enough but future features may require
+additional reserved inodes. Reserving more inodes after file system is created
+requires full file system scan so it can take a long time.
.RE
.TP
.BI \-f " fragment-size"
diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
index 06ca9e4eabc4..b35767bfedf2 100644
--- a/misc/mke2fs.conf.5.in
+++ b/misc/mke2fs.conf.5.in
@@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
.TP
.I reserved_inodes
This relation specifies the default number of inodes reserved for system files.
-The number must be at least 10. Currently 10 is enough but future features may
-require additional reserved inodes. Reserving more inodes after file system is
-created requires full file system scan so it can take a long time.
+The number must be at least 10, default is 64. Currently 10 is enough but
+future features may require additional reserved inodes. Reserving more inodes
+after file system is created requires full file system scan so it can take a
+long time.
.TP
.I undo_dir
This relation specifies the directory where the undo file should be
--
2.1.4


2015-08-07 10:51:50

by Jan Kara

[permalink] [raw]
Subject: [PATCH 19/19] resize2fs: Use libextfs2 helpers for resizing

Signed-off-by: Jan Kara <[email protected]>
---
resize/online.c | 14 +-
resize/resize2fs.c | 2307 ++++++++++++----------------------------------------
resize/resize2fs.h | 15 +-
3 files changed, 551 insertions(+), 1785 deletions(-)

diff --git a/resize/online.c b/resize/online.c
index 46d86b0f3c8d..d202c0e92e6e 100644
--- a/resize/online.c
+++ b/resize/online.c
@@ -197,6 +197,12 @@ errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt,
return retval;
}

+ retval = validate_fs_size(fs, new_size);
+ if (retval) {
+ close(fd);
+ return retval;
+ }
+
retval = ext2fs_dup_handle(fs, &new_fs);
if (retval) {
close(fd);
@@ -214,7 +220,13 @@ errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt,
* but at least it allows on-line resizing to function.
*/
new_fs->super->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_FLEX_BG;
- retval = adjust_fs_info(new_fs, fs, 0, *new_size);
+ retval = adjust_fs_info(new_fs, fs, *new_size);
+ if (retval) {
+ close(fd);
+ return retval;
+ }
+
+ retval = alloc_new_groups(new_fs, fs->group_desc_count);
if (retval) {
close(fd);
return retval;
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 22300836300d..8531c59b859b 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -13,24 +13,36 @@
*/

/*
- * Resizing a filesystem consists of the following phases:
+ * Shrinking a filesystem consists of the following phases:
*
- * 1. Adjust superblock and write out new parts of the inode
- * table
- * 2. Determine blocks which need to be relocated, and copy the
- * contents of blocks from their old locations to the new ones.
- * 3. Scan the inode table, doing the following:
- * a. If blocks have been moved, update the block
- * pointers in the inodes and indirect blocks to
- * point at the new block locations.
- * b. If parts of the inode table need to be evacuated,
- * copy inodes from their old locations to their
- * new ones.
- * c. If (b) needs to be done, note which blocks contain
- * directory information, since we will need to
- * update the directory information.
- * 4. Update the directory blocks with the new inode locations.
- * 5. Move the inode tables, if necessary.
+ * 1. We validate whether filesystem size is sane
+ * 2. We identify blocks which need moving (beyond new end of the
+ * filesystem, under new sb backup if sparse_super2 is enabled) and
+ * move them. ext2fs_move_blocks() also takes care of updating all the
+ * block references.
+ * 3. We identify inodes which need moving (beyond end of the new
+ * filesystem) and move them. ext2fs_move_inodes() also takes care of
+ * updating all the inode references.
+ * 4. We create new fs structure with updated number of block groups.
+ * 5. We free metadata of groups that will be removed (with flex_bg
+ * feature they can be allocated inside area of the new filesystem).
+ * 6. We free backup group descriptor blocks which are not needed anymore.
+ * 7. We recompute group & global allocation counters.
+ * 8. We rewrite resize inode to contain now reserved blocks for backup
+ * group descriptors.
+ *
+ * Growing a filesystem consists of the following phases:
+ * 1. We validate whether filesystem size is sane
+ * 2. We create new fs structure with updated number of block groups.
+ * 3. We allocate and initialize bitmaps & inode tables for new groups.
+ * 4. We identify blocks that need moving to make space for new group
+ * descriptor backups and move them. ext2fs_move_blocks() also takes
+ * care of updating all the block references.
+ * 5. We free space of group descriptor backups that aren't needed anymore
+ * (happens with sparse_super2 feature).
+ * 6. We recompute group & global allocation counters.
+ * 7. We rewrite resize inode to contain now reserved blocks for backup
+ * group descriptors.
*/

#include "config.h"
@@ -41,20 +53,16 @@
#define RESIZE2FS_DEBUG
#endif

-static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size);
-static errcode_t blocks_to_move(ext2_resize_t rfs);
-static errcode_t block_mover(ext2_resize_t rfs);
-static errcode_t inode_scan_and_fix(ext2_resize_t rfs);
-static errcode_t inode_ref_fix(ext2_resize_t rfs);
-static errcode_t move_itables(ext2_resize_t rfs);
static errcode_t fix_resize_inode(ext2_filsys fs);
static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs);
-static errcode_t fix_sb_journal_backup(ext2_filsys fs);
static errcode_t mark_table_blocks(ext2_filsys fs,
ext2fs_block_bitmap bmap);
static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs);
-static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
- ext2fs_block_bitmap meta_bmap);
+static errcode_t free_group_metadata(ext2_resize_t rfs);
+static errcode_t prepare_shrink(ext2_filsys fs, blk64_t new_size);
+static errcode_t init_new_itables(ext2_resize_t rfs);
+static errcode_t free_gd_blocks(ext2_resize_t rfs);
+static errcode_t make_space_for_gd(ext2_resize_t rfs);

/*
* Some helper CPP macros
@@ -100,7 +108,6 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
fs->priv_data = rfs;
rfs->old_fs = fs;
rfs->flags = flags;
- rfs->itable_buf = 0;
rfs->progress = progress;

init_resource_track(&overall_track, "overall resize2fs", fs->io);
@@ -117,62 +124,74 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
init_resource_track(&rtrack, "fix_uninit_block_bitmaps 1", fs->io);
ext2fs_init_uninit_block_bitmaps(fs);
print_resource_track(rfs, &rtrack, fs->io);
- retval = ext2fs_dup_handle(fs, &rfs->new_fs);
- if (retval)
- goto errout;

- init_resource_track(&rtrack, "adjust_superblock", fs->io);
- retval = adjust_superblock(rfs, *new_size);
+ retval = validate_fs_size(fs, new_size);
if (retval)
goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
- init_resource_track(&rtrack, "fix_uninit_block_bitmaps 2", fs->io);
- ext2fs_init_uninit_block_bitmaps(rfs->new_fs);
- print_resource_track(rfs, &rtrack, fs->io);
- /* Clear the block bitmap uninit flag for the last block group */
- ext2fs_bg_flags_clear(rfs->new_fs, rfs->new_fs->group_desc_count - 1,
- EXT2_BG_BLOCK_UNINIT);

- *new_size = ext2fs_blocks_count(rfs->new_fs->super);
+ if (*new_size < ext2fs_blocks_count(fs->super)) {
+ init_resource_track(&rtrack, "prepare_shrink", fs->io);
+ retval = prepare_shrink(fs, *new_size);
+ if (retval)
+ goto errout;
+ print_resource_track(rfs, &rtrack, fs->io);
+ }

- init_resource_track(&rtrack, "blocks_to_move", fs->io);
- retval = blocks_to_move(rfs);
+ retval = ext2fs_dup_handle(fs, &rfs->new_fs);
if (retval)
goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_BMOVE)
- printf("Number of free blocks: %llu/%llu, Needed: %llu\n",
- ext2fs_free_blocks_count(rfs->old_fs->super),
- ext2fs_free_blocks_count(rfs->new_fs->super),
- rfs->needed_blocks);
-#endif

- init_resource_track(&rtrack, "block_mover", fs->io);
- retval = block_mover(rfs);
+ init_resource_track(&rtrack, "adjust_fs_info", fs->io);
+ retval = adjust_fs_info(rfs->new_fs, fs, *new_size);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);

- init_resource_track(&rtrack, "inode_scan_and_fix", fs->io);
- retval = inode_scan_and_fix(rfs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
+ if (*new_size > ext2fs_blocks_count(fs->super)) {
+ retval = alloc_new_groups(rfs->new_fs,
+ rfs->old_fs->group_desc_count);
+ if (retval)
+ goto errout;

- init_resource_track(&rtrack, "inode_ref_fix", fs->io);
- retval = inode_ref_fix(rfs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
+ retval = init_new_itables(rfs);
+ if (retval)
+ goto errout;

- init_resource_track(&rtrack, "move_itables", fs->io);
- retval = move_itables(rfs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
+ init_resource_track(&rtrack, "fix_uninit_block_bitmaps 2",
+ fs->io);
+ ext2fs_init_uninit_block_bitmaps(rfs->new_fs);
+ print_resource_track(rfs, &rtrack, fs->io);
+ }
+
+ /* Clear the block bitmap uninit flag for the last block group */
+ ext2fs_bg_flags_clear(rfs->new_fs, rfs->new_fs->group_desc_count - 1,
+ EXT2_BG_BLOCK_UNINIT);
+
+ if (*new_size > ext2fs_blocks_count(fs->super)) {
+ init_resource_track(&rtrack, "make_space_for_gd", fs->io);
+ retval = make_space_for_gd(rfs);
+ if (retval)
+ goto errout;
+ print_resource_track(rfs, &rtrack, fs->io);
+
+ retval = clear_sparse_super2_last_group(rfs);
+ if (retval)
+ goto errout;
+ } else {
+ init_resource_track(&rtrack, "free_group_metadata", fs->io);
+ /* Now free metadata of groups we don't need anymore */
+ retval = free_group_metadata(rfs);
+ if (retval)
+ goto errout;
+ print_resource_track(rfs, &rtrack, fs->io);
+
+ init_resource_track(&rtrack, "free_gd_blocks", fs->io);
+ /* Free backup group descriptors we don't need */
+ retval = free_gd_blocks(rfs);
+ if (retval)
+ goto errout;
+ print_resource_track(rfs, &rtrack, fs->io);
+ }

init_resource_track(&rtrack, "calculate_summary_stats", fs->io);
retval = ext2fs_calculate_summary_stats(rfs->new_fs);
@@ -186,22 +205,15 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
goto errout;
print_resource_track(rfs, &rtrack, fs->io);

- init_resource_track(&rtrack, "fix_sb_journal_backup", fs->io);
- retval = fix_sb_journal_backup(rfs->new_fs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
- retval = clear_sparse_super2_last_group(rfs);
- if (retval)
- goto errout;
-
retval = ext2fs_set_gdt_csum(rfs->new_fs);
if (retval)
goto errout;

rfs->new_fs->super->s_state &= ~EXT2_ERROR_FS;
rfs->new_fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ ext2fs_mark_bb_dirty(rfs->new_fs);
+ ext2fs_mark_ib_dirty(rfs->new_fs);
+ ext2fs_mark_super_dirty(rfs->new_fs);

print_resource_track(rfs, &overall_track, fs->io);
retval = ext2fs_close_free(&rfs->new_fs);
@@ -212,12 +224,6 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,

ext2fs_free(rfs->old_fs);
rfs->old_fs = NULL;
- if (rfs->itable_buf)
- ext2fs_free_mem(&rfs->itable_buf);
- if (rfs->reserve_blocks)
- ext2fs_free_block_bitmap(rfs->reserve_blocks);
- if (rfs->move_blocks)
- ext2fs_free_block_bitmap(rfs->move_blocks);
ext2fs_free_mem(&rfs);

return 0;
@@ -227,8 +233,6 @@ errout:
ext2fs_free(rfs->new_fs);
rfs->new_fs = NULL;
}
- if (rfs->itable_buf)
- ext2fs_free_mem(&rfs->itable_buf);
ext2fs_free_mem(&rfs);
return retval;
}
@@ -263,21 +267,73 @@ static void adjust_reserved_gdt_blocks(ext2_filsys old_fs, ext2_filsys fs)
*/

/*
- * If the group descriptor's bitmap and inode table blocks are valid,
- * release them in the new filesystem data structure, and mark them as
- * reserved so the old inode table blocks don't get overwritten.
+ * Check whether requested filesystem size is sane. Round it down in case the
+ * last group would be too small.
+ */
+errcode_t validate_fs_size(ext2_filsys fs, blk64_t *new_sizep)
+{
+ blk64_t new_size = *new_sizep;
+ dgrp_t groups;
+ blk64_t desc_blocks;
+ blk64_t overhead = 0, rem;
+ unsigned long long new_inodes; /* u64 to check for overflow */
+
+retry:
+ groups = ext2fs_div64_ceil(new_size - fs->super->s_first_data_block,
+ EXT2_BLOCKS_PER_GROUP(fs->super));
+ if (groups == 0)
+ return EXT2_ET_TOOSMALL;
+ desc_blocks = ext2fs_div_ceil(groups, EXT2_DESC_PER_BLOCK(fs->super));
+
+ /*
+ * Overhead is the number of bookkeeping blocks per group. It
+ * includes the superblock backup, the group descriptor
+ * backups, the inode bitmap, the block bitmap, and the inode
+ * table.
+ */
+ overhead = 2 + fs->inode_blocks_per_group;
+ if (ext2fs_bg_has_super(fs, groups - 1))
+ overhead += 1 + desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+ /*
+ * See if the last group is big enough to support the
+ * necessary data structures. If not, we need to get rid of
+ * it.
+ */
+ rem = (new_size - fs->super->s_first_data_block) %
+ fs->super->s_blocks_per_group;
+ if (groups == 1 && rem && rem < overhead)
+ return EXT2_ET_TOOSMALL;
+ if (groups > 1 && rem && rem < overhead + 50) {
+ new_size -= rem;
+ goto retry;
+ }
+
+ new_inodes = ((unsigned long long)fs->super->s_inodes_per_group) *
+ groups;
+ if (new_inodes > ~0U) {
+ fprintf(stderr, _("inodes (%llu) must be less than %u"),
+ new_inodes, ~0U);
+ return EXT2_ET_TOO_MANY_INODES;
+ }
+
+ *new_sizep = new_size;
+ return 0;
+}
+
+/*
+ * If the group descriptor's bitmap and inode table blocks are valid, release
+ * them. With FLEX_BG this may actually free some space in the shrunk
+ * filesystem.
*/
-static errcode_t free_gdp_blocks(ext2_filsys fs,
- ext2fs_block_bitmap reserve_blocks,
- ext2_filsys old_fs,
- dgrp_t group)
+static errcode_t free_group_metadata(ext2_resize_t rfs)
{
+ ext2_filsys fs = rfs->new_fs, old_fs = rfs->old_fs;
blk64_t blk;
int j;
dgrp_t i;
ext2fs_block_bitmap bg_map = NULL;
errcode_t retval = 0;
- dgrp_t count = old_fs->group_desc_count - fs->group_desc_count;

/* If bigalloc, don't free metadata living in the same cluster */
if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
@@ -290,22 +346,18 @@ static errcode_t free_gdp_blocks(ext2_filsys fs,
goto out;
}

- for (i = group; i < group + count; i++) {
+ for (i = fs->group_desc_count; i < old_fs->group_desc_count; i++) {
blk = ext2fs_block_bitmap_loc(old_fs, i);
if (blk &&
(blk < ext2fs_blocks_count(fs->super)) &&
- !(bg_map && ext2fs_test_block_bitmap2(bg_map, blk))) {
+ !(bg_map && ext2fs_test_block_bitmap2(bg_map, blk)))
ext2fs_block_alloc_stats2(fs, blk, -1);
- ext2fs_mark_block_bitmap2(reserve_blocks, blk);
- }

blk = ext2fs_inode_bitmap_loc(old_fs, i);
if (blk &&
(blk < ext2fs_blocks_count(fs->super)) &&
- !(bg_map && ext2fs_test_block_bitmap2(bg_map, blk))) {
+ !(bg_map && ext2fs_test_block_bitmap2(bg_map, blk)))
ext2fs_block_alloc_stats2(fs, blk, -1);
- ext2fs_mark_block_bitmap2(reserve_blocks, blk);
- }

blk = ext2fs_inode_table_loc(old_fs, i);
for (j = 0;
@@ -314,7 +366,6 @@ static errcode_t free_gdp_blocks(ext2_filsys fs,
(bg_map && ext2fs_test_block_bitmap2(bg_map, blk)))
continue;
ext2fs_block_alloc_stats2(fs, blk, -1);
- ext2fs_mark_block_bitmap2(reserve_blocks, blk);
}
}

@@ -325,250 +376,180 @@ out:
}

/*
- * This routine is shared by the online and offline resize routines.
- * All of the information which is adjusted in memory is done here.
+ * Prepare filesystem for shrinking - free up all space beyond new end of the
+ * filesystem and space needed for new group descriptor backups. Also move
+ * inodes that are in removed block groups into block groups that remain.
*/
-errcode_t adjust_fs_info(ext2_filsys fs, ext2_filsys old_fs,
- ext2fs_block_bitmap reserve_blocks, blk64_t new_size)
+static errcode_t prepare_shrink(ext2_filsys fs, blk64_t new_size)
{
+ dgrp_t groups, i;
+ blk64_t blk;
+ blk64_t blk_per_g = EXT2_BLOCKS_PER_GROUP(fs->super);
+ ext2fs_inode_bitmap move_inodes = NULL;
+ ext2fs_block_bitmap move_blocks = NULL;
+ ext2_ino_t old_inos, new_inos, ino;
errcode_t retval;
- blk64_t overhead = 0;
- blk64_t rem;
- blk64_t blk, group_block;
- blk64_t real_end;
- blk64_t old_numblocks, numblocks, adjblocks;
- unsigned long i, j, old_desc_blocks;
- unsigned int meta_bg, meta_bg_size;
- int has_super, csum_flag;
- unsigned long long new_inodes; /* u64 to check for overflow */
- double percent;
-
- ext2fs_blocks_count_set(fs->super, new_size);
-
-retry:
- fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) -
- fs->super->s_first_data_block,
- EXT2_BLOCKS_PER_GROUP(fs->super));
- if (fs->group_desc_count == 0)
- return EXT2_ET_TOOSMALL;
- fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
- EXT2_DESC_PER_BLOCK(fs->super));
-
- /*
- * Overhead is the number of bookkeeping blocks per group. It
- * includes the superblock backup, the group descriptor
- * backups, the inode bitmap, the block bitmap, and the inode
- * table.
- */
- overhead = (int) (2 + fs->inode_blocks_per_group);

- if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
- overhead += 1 + fs->desc_blocks +
- fs->super->s_reserved_gdt_blocks;
+ groups = ext2fs_div64_ceil(new_size - fs->super->s_first_data_block,
+ blk_per_g);

+ retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
+ &move_blocks);
+ if (retval)
+ goto errout;
/*
- * See if the last group is big enough to support the
- * necessary data structures. If not, we need to get rid of
- * it.
- */
- rem = (ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block) %
- fs->super->s_blocks_per_group;
- if ((fs->group_desc_count == 1) && rem && (rem < overhead))
- return EXT2_ET_TOOSMALL;
- if ((fs->group_desc_count > 1) && rem && (rem < overhead+50)) {
- ext2fs_blocks_count_set(fs->super,
- ext2fs_blocks_count(fs->super) - rem);
- goto retry;
- }
- /*
- * Adjust the number of inodes
+ * Update the location of the backup superblocks if the sparse_super2
+ * feature is enabled. Mark new backup descriptor blocks in bitmap
+ * to move
*/
- new_inodes =(unsigned long long) fs->super->s_inodes_per_group * fs->group_desc_count;
- if (new_inodes > ~0U) {
- fprintf(stderr, _("inodes (%llu) must be less than %u"),
- new_inodes, ~0U);
- return EXT2_ET_TOO_MANY_INODES;
+ if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2 &&
+ groups < fs->group_desc_count) {
+ dgrp_t last_bg = groups - 1;
+ dgrp_t old_last_bg = fs->group_desc_count - 1;
+
+ if (last_bg > 1 && fs->super->s_backup_bgs[1] == old_last_bg) {
+ blk64_t desc_blocks = 0;
+ dgrp_t meta_bg, meta_bg_size;
+
+ meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
+ meta_bg = last_bg / meta_bg_size;
+
+ if (!(fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_META_BG) ||
+ (meta_bg < fs->super->s_first_meta_bg)) {
+ desc_blocks = ext2fs_div_ceil(groups,
+ EXT2_DESC_PER_BLOCK(fs->super));
+ } else {
+ if (((last_bg % meta_bg_size) == 0) ||
+ ((last_bg % meta_bg_size) == 1) ||
+ ((last_bg % meta_bg_size) == (meta_bg_size-1)))
+ desc_blocks = 1;
+ }
+ fs->super->s_backup_bgs[1] = last_bg;
+ ext2fs_mark_block_bitmap_range2(move_blocks,
+ last_bg * blk_per_g + fs->super->s_first_data_block,
+ 1 + desc_blocks);
+ }
+ if (fs->super->s_backup_bgs[0] > last_bg)
+ fs->super->s_backup_bgs[0] = 0;
+ if (fs->super->s_backup_bgs[1] > last_bg)
+ fs->super->s_backup_bgs[1] = 0;
}
- fs->super->s_inodes_count = fs->super->s_inodes_per_group *
- fs->group_desc_count;
-
- /*
- * Adjust the number of free blocks
- */
- blk = ext2fs_blocks_count(old_fs->super);
- if (blk > ext2fs_blocks_count(fs->super))
- ext2fs_free_blocks_count_set(fs->super,
- ext2fs_free_blocks_count(fs->super) -
- (blk - ext2fs_blocks_count(fs->super)));
- else
- ext2fs_free_blocks_count_set(fs->super,
- ext2fs_free_blocks_count(fs->super) +
- (ext2fs_blocks_count(fs->super) - blk));
-
- /*
- * Adjust the number of reserved blocks
- */
- percent = (ext2fs_r_blocks_count(old_fs->super) * 100.0) /
- ext2fs_blocks_count(old_fs->super);
- ext2fs_r_blocks_count_set(fs->super,
- (percent * ext2fs_blocks_count(fs->super) /
- 100.0));

+ /* Mark all blocks beyond new end of fs in bitmap to move */
+ ext2fs_mark_block_bitmap_range2(move_blocks, new_size,
+ ext2fs_blocks_count(fs->super) - new_size);
/*
- * Adjust the bitmaps for size
+ * Now have to we unmark metadata of groups which are going away since
+ * ext2fs_move_blocks() would allocate blocks for them
*/
- retval = ext2fs_resize_inode_bitmap2(fs->super->s_inodes_count,
- fs->super->s_inodes_count,
- fs->inode_map);
- if (retval) goto errout;
-
- real_end = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count) - 1 +
- fs->super->s_first_data_block;
- retval = ext2fs_resize_block_bitmap2(new_size - 1,
- real_end, fs->block_map);
- if (retval) goto errout;
+ for (i = groups; i < fs->group_desc_count; i++) {
+ blk_t num_blocks;
+ blk64_t old_desc_blk, new_desc_blk;
+
+ ext2fs_super_and_bgd_loc2(fs, i, &blk, &old_desc_blk,
+ &new_desc_blk, &num_blocks);
+ if (blk) {
+ ext2fs_unmark_block_bitmap2(move_blocks, blk);
+ num_blocks--;
+ }
+ if (old_desc_blk)
+ blk = old_desc_blk;
+ else
+ blk = new_desc_blk;

- /*
- * If we are growing the file system, also grow the size of
- * the reserve_blocks bitmap
- */
- if (reserve_blocks && new_size > ext2fs_blocks_count(old_fs->super)) {
- retval = ext2fs_resize_block_bitmap2(new_size - 1,
- real_end, reserve_blocks);
- if (retval) goto errout;
- }
+ if (blk)
+ ext2fs_unmark_block_bitmap_range2(move_blocks, blk,
+ num_blocks);

- /*
- * Reallocate the group descriptors as necessary.
- */
- if (old_fs->desc_blocks != fs->desc_blocks) {
- retval = ext2fs_resize_mem(old_fs->desc_blocks *
- fs->blocksize,
- fs->desc_blocks * fs->blocksize,
- &fs->group_desc);
- if (retval)
- goto errout;
- if (fs->desc_blocks > old_fs->desc_blocks)
- memset((char *) fs->group_desc +
- (old_fs->desc_blocks * fs->blocksize), 0,
- (fs->desc_blocks - old_fs->desc_blocks) *
- fs->blocksize);
- }
+ /*
+ * Mark the blocks used for the inode table
+ */
+ blk = ext2fs_inode_table_loc(fs, i);
+ if (blk)
+ ext2fs_unmark_block_bitmap_range2(move_blocks, blk,
+ fs->inode_blocks_per_group);

- /*
- * If the resize_inode feature is set, and we are changing the
- * number of descriptor blocks, then adjust
- * s_reserved_gdt_blocks if possible to avoid needing to move
- * the inode table either now or in the future.
- */
- adjust_reserved_gdt_blocks(old_fs, fs);
+ /*
+ * Mark block used for the block bitmap
+ */
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_unmark_block_bitmap2(move_blocks, blk);

- if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
- (fs->super->s_first_meta_bg > fs->desc_blocks)) {
- fs->super->s_feature_incompat &=
- ~EXT2_FEATURE_INCOMPAT_META_BG;
- fs->super->s_first_meta_bg = 0;
+ /*
+ * Mark block used for the inode bitmap
+ */
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_unmark_block_bitmap2(move_blocks, blk);
}

- /*
- * Update the location of the backup superblocks if the
- * sparse_super2 feature is enabled.
- */
- if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
- dgrp_t last_bg = fs->group_desc_count - 1;
- dgrp_t old_last_bg = old_fs->group_desc_count - 1;
+ /* Map of blocks to move is done - let's move them */
+ retval = ext2fs_move_blocks(fs, move_blocks, NULL);
+ if (retval)
+ goto errout;

- if (last_bg > old_last_bg) {
- if (old_fs->group_desc_count == 1)
- fs->super->s_backup_bgs[0] = 1;
- if (old_fs->group_desc_count == 1 &&
- fs->super->s_backup_bgs[0])
- fs->super->s_backup_bgs[0] = last_bg;
- else if (fs->super->s_backup_bgs[1])
- fs->super->s_backup_bgs[1] = last_bg;
- } else if (last_bg < old_last_bg) {
- if (fs->super->s_backup_bgs[0] > last_bg)
- fs->super->s_backup_bgs[0] = 0;
- if (fs->super->s_backup_bgs[1] > last_bg)
- fs->super->s_backup_bgs[1] = 0;
- if (last_bg > 1 &&
- old_fs->super->s_backup_bgs[1] == old_last_bg)
- fs->super->s_backup_bgs[1] = last_bg;
- }
- }
+ /* Didn't remove any group? No need to move any inodes so we are done */
+ if (groups == fs->group_desc_count)
+ goto errout;

- /*
- * If we are shrinking the number of block groups, we're done
- * and can exit now.
- */
- if (old_fs->group_desc_count > fs->group_desc_count) {
- /*
- * Check the block groups that we are chopping off
- * and free any blocks associated with their metadata
- */
- retval = free_gdp_blocks(fs, reserve_blocks, old_fs,
- fs->group_desc_count);
+ /* Move inodes from groups that will be removed */
+ retval = ext2fs_allocate_inode_bitmap(fs, _("inodes to be moved"),
+ &move_inodes);
+ if (retval)
goto errout;
- }

- /*
- * Fix the count of the last (old) block group
- */
- old_numblocks = (ext2fs_blocks_count(old_fs->super) -
- old_fs->super->s_first_data_block) %
- old_fs->super->s_blocks_per_group;
- if (!old_numblocks)
- old_numblocks = old_fs->super->s_blocks_per_group;
- if (old_fs->group_desc_count == fs->group_desc_count) {
- numblocks = (ext2fs_blocks_count(fs->super) -
- fs->super->s_first_data_block) %
- fs->super->s_blocks_per_group;
- if (!numblocks)
- numblocks = fs->super->s_blocks_per_group;
- } else
- numblocks = fs->super->s_blocks_per_group;
- i = old_fs->group_desc_count - 1;
- ext2fs_bg_free_blocks_count_set(fs, i, ext2fs_bg_free_blocks_count(fs, i) + (numblocks - old_numblocks));
- ext2fs_group_desc_csum_set(fs, i);
+ old_inos = fs->group_desc_count * fs->super->s_inodes_per_group;
+ new_inos = groups * fs->super->s_inodes_per_group;
+ for (ino = new_inos; ino < old_inos; ino++)
+ ext2fs_mark_inode_bitmap2(move_inodes, ino);

- /*
- * If the number of block groups is staying the same, we're
- * done and can exit now. (If the number block groups is
- * shrinking, we had exited earlier.)
- */
- if (old_fs->group_desc_count >= fs->group_desc_count) {
- retval = 0;
- goto errout;
- }
+ retval = ext2fs_move_inodes(fs, move_inodes);
+errout:
+ if (move_inodes)
+ ext2fs_free_inode_bitmap(move_inodes);
+ if (move_blocks)
+ ext2fs_free_block_bitmap(move_blocks);
+ return retval;
+}
+
+/*
+ * Allocate space for group metadata (sb & descriptor backups, bitmaps,
+ * inode table)
+ */
+errcode_t alloc_new_groups(ext2_filsys fs, dgrp_t from)
+{
+ errcode_t retval;
+ unsigned long i;
+ int csum_flag;
+ ext2fs_block_bitmap merged_map = NULL;

/*
* Initialize the new block group descriptors
*/
- group_block = ext2fs_group_first_block2(fs,
- old_fs->group_desc_count);
csum_flag = ext2fs_has_group_desc_csum(fs);
if (!getenv("RESIZE2FS_FORCE_ITABLE_INIT") &&
access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0)
lazy_itable_init = 1;
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- old_desc_blocks = fs->super->s_first_meta_bg;
- else
- old_desc_blocks = fs->desc_blocks +
- fs->super->s_reserved_gdt_blocks;
+
+ retval = ext2fs_copy_bitmap(fs->block_map, &merged_map);
+ if (retval)
+ return retval;

/*
* If we changed the number of block_group descriptor blocks,
- * we need to make sure they are all marked as reserved in the
- * file systems's block allocation map.
+ * we need to make sure they are all marked as reserved so that
+ * ext2fs_allocate_group_table2() called a bit later doesn't use
+ * these blocks for new group's metadata. We mark the blocks as
+ * used in the block bitmap once we free them up.
*/
- for (i = 0; i < old_fs->group_desc_count; i++)
- ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
+ for (i = 0; i < from; i++)
+ ext2fs_reserve_super_and_bgd(fs, i, merged_map);

- for (i = old_fs->group_desc_count;
- i < fs->group_desc_count; i++) {
+ for (i = from; i < fs->group_desc_count; i++) {
memset(ext2fs_group_desc(fs, fs->group_desc, i), 0,
sizeof(struct ext2_group_desc));
- adjblocks = 0;

ext2fs_bg_flags_zap(fs, i);
if (csum_flag) {
@@ -580,115 +561,34 @@ retry:
fs->super->s_inodes_per_group);
}

- numblocks = ext2fs_group_blocks_count(fs, i);
- if ((i < fs->group_desc_count - 1) && csum_flag)
+ if ((i < fs->group_desc_count - 1) && csum_flag &&
+ !ext2fs_bg_has_super(fs, i))
ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT);

- has_super = ext2fs_bg_has_super(fs, i);
- if (has_super) {
- ext2fs_block_alloc_stats2(fs, group_block, +1);
- adjblocks++;
- }
- meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
- meta_bg = i / meta_bg_size;
- if (!(fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG) ||
- (meta_bg < fs->super->s_first_meta_bg)) {
- if (has_super) {
- for (j=0; j < old_desc_blocks; j++)
- ext2fs_block_alloc_stats2(fs,
- group_block + 1 + j, +1);
- adjblocks += old_desc_blocks;
- }
- } else {
- if (has_super)
- has_super = 1;
- if (((i % meta_bg_size) == 0) ||
- ((i % meta_bg_size) == 1) ||
- ((i % meta_bg_size) == (meta_bg_size-1)))
- ext2fs_block_alloc_stats2(fs,
- group_block + has_super, +1);
- }
-
- adjblocks += 2 + fs->inode_blocks_per_group;
-
- numblocks -= adjblocks;
- ext2fs_free_blocks_count_set(fs->super,
- ext2fs_free_blocks_count(fs->super) - adjblocks);
- fs->super->s_free_inodes_count +=
- fs->super->s_inodes_per_group;
- ext2fs_bg_free_blocks_count_set(fs, i, numblocks);
- ext2fs_bg_free_inodes_count_set(fs, i,
- fs->super->s_inodes_per_group);
ext2fs_bg_used_dirs_count_set(fs, i, 0);
- ext2fs_group_desc_csum_set(fs, i);

- retval = ext2fs_allocate_group_table(fs, i, 0);
- if (retval) goto errout;
+ ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
+ ext2fs_reserve_super_and_bgd(fs, i, merged_map);

- group_block += fs->super->s_blocks_per_group;
+ retval = ext2fs_allocate_group_table2(fs, i, merged_map,
+ EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP);
+ if (retval)
+ goto errout;
}
- retval = 0;
-
- /*
- * Mark all of the metadata blocks as reserved so they won't
- * get allocated by the call to ext2fs_allocate_group_table()
- * in blocks_to_move(), where we allocate new blocks to
- * replace those allocation bitmap and inode table blocks
- * which have to get relocated to make space for an increased
- * number of the block group descriptors.
- */
- if (reserve_blocks)
- mark_table_blocks(fs, reserve_blocks);

errout:
- return (retval);
+ if (merged_map)
+ ext2fs_free_block_bitmap(merged_map);
+ return retval;
}

-/*
- * This routine adjusts the superblock and other data structures, both
- * in disk as well as in memory...
- */
-static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
+/* Initialize inode tables in new block groups. */
+static errcode_t init_new_itables(ext2_resize_t rfs)
{
- ext2_filsys fs = rfs->new_fs;
- int adj = 0;
errcode_t retval;
- blk64_t group_block;
+ ext2_filsys old_fs = rfs->old_fs, fs = rfs->new_fs;
+ dgrp_t max_group, adj;
unsigned long i;
- unsigned long max_group;
-
- ext2fs_mark_super_dirty(fs);
- ext2fs_mark_bb_dirty(fs);
- ext2fs_mark_ib_dirty(fs);
-
- retval = ext2fs_allocate_block_bitmap(fs, _("reserved blocks"),
- &rfs->reserve_blocks);
- if (retval)
- return retval;
-
- retval = adjust_fs_info(fs, rfs->old_fs, rfs->reserve_blocks, new_size);
- if (retval)
- goto errout;
-
- /*
- * Check to make sure there are enough inodes
- */
- if ((rfs->old_fs->super->s_inodes_count -
- rfs->old_fs->super->s_free_inodes_count) >
- rfs->new_fs->super->s_inodes_count) {
- retval = ENOSPC;
- goto errout;
- }
-
- /*
- * If we are not increasing the number block groups, we're done and
- * can exit now.
- */
- if (rfs->old_fs->group_desc_count >= fs->group_desc_count) {
- retval = 0;
- goto errout;
- }

/*
* If we are using uninit_bg (aka GDT_CSUM) and the kernel
@@ -700,18 +600,7 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
goto errout;
}

- /*
- * Initialize the inode table
- */
- retval = ext2fs_get_array(fs->blocksize, fs->inode_blocks_per_group,
- &rfs->itable_buf);
- if (retval)
- goto errout;
-
- memset(rfs->itable_buf, 0, fs->blocksize * fs->inode_blocks_per_group);
- group_block = ext2fs_group_first_block2(fs,
- rfs->old_fs->group_desc_count);
- adj = rfs->old_fs->group_desc_count;
+ adj = old_fs->group_desc_count;
max_group = fs->group_desc_count - adj;
if (rfs->progress) {
retval = rfs->progress(rfs, E2_RSZ_EXTEND_ITABLE_PASS,
@@ -719,8 +608,7 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
if (retval)
goto errout;
}
- for (i = rfs->old_fs->group_desc_count;
- i < fs->group_desc_count; i++) {
+ for (i = old_fs->group_desc_count; i < fs->group_desc_count; i++) {
/*
* Write out the new inode table
*/
@@ -737,1320 +625,320 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
if (retval)
goto errout;
}
- group_block += fs->super->s_blocks_per_group;
}
io_channel_flush(fs->io);
retval = 0;
-
errout:
- return retval;
-}
-
-/* --------------------------------------------------------------------
- *
- * Resize processing, phase 2.
- *
- * In this phase we adjust determine which blocks need to be moved, in
- * blocks_to_move(). We then copy the blocks to their ultimate new
- * destinations using block_mover(). Since we are copying blocks to
- * their new locations, again during this pass we can abort without
- * any problems.
- * --------------------------------------------------------------------
- */
-
-/*
- * This helper function creates a block bitmap with all of the
- * filesystem meta-data blocks.
- */
-static errcode_t mark_table_blocks(ext2_filsys fs,
- ext2fs_block_bitmap bmap)
-{
- dgrp_t i;
- blk64_t blk;
-
- for (i = 0; i < fs->group_desc_count; i++) {
- ext2fs_reserve_super_and_bgd(fs, i, bmap);
-
- /*
- * Mark the blocks used for the inode table
- */
- blk = ext2fs_inode_table_loc(fs, i);
- if (blk)
- ext2fs_mark_block_bitmap_range2(bmap, blk,
- fs->inode_blocks_per_group);
-
- /*
- * Mark block used for the block bitmap
- */
- blk = ext2fs_block_bitmap_loc(fs, i);
- if (blk)
- ext2fs_mark_block_bitmap2(bmap, blk);
-
- /*
- * Mark block used for the inode bitmap
- */
- blk = ext2fs_inode_bitmap_loc(fs, i);
- if (blk)
- ext2fs_mark_block_bitmap2(bmap, blk);
- }
- return 0;
-}
-
-/*
- * This function checks to see if a particular block (either a
- * superblock or a block group descriptor) overlaps with an inode or
- * block bitmap block, or with the inode table.
- */
-static void mark_fs_metablock(ext2_resize_t rfs,
- ext2fs_block_bitmap meta_bmap,
- int group, blk64_t blk)
-{
- ext2_filsys fs = rfs->new_fs;
-
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
- ext2fs_block_alloc_stats2(fs, blk, +1);
-
- /*
- * Check to see if we overlap with the inode or block bitmap,
- * or the inode tables. If not, and the block is in use, then
- * mark it as a block to be moved.
- */
- if (IS_BLOCK_BM(fs, group, blk)) {
- ext2fs_block_bitmap_loc_set(fs, group, 0);
- rfs->needed_blocks++;
- return;
- }
- if (IS_INODE_BM(fs, group, blk)) {
- ext2fs_inode_bitmap_loc_set(fs, group, 0);
- rfs->needed_blocks++;
- return;
- }
- if (IS_INODE_TB(fs, group, blk)) {
- ext2fs_inode_table_loc_set(fs, group, 0);
- rfs->needed_blocks++;
- return;
- }
- if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
- dgrp_t i;
-
- for (i=0; i < rfs->old_fs->group_desc_count; i++) {
- if (IS_BLOCK_BM(fs, i, blk)) {
- ext2fs_block_bitmap_loc_set(fs, i, 0);
- rfs->needed_blocks++;
- return;
- }
- if (IS_INODE_BM(fs, i, blk)) {
- ext2fs_inode_bitmap_loc_set(fs, i, 0);
- rfs->needed_blocks++;
- return;
- }
- if (IS_INODE_TB(fs, i, blk)) {
- ext2fs_inode_table_loc_set(fs, i, 0);
- rfs->needed_blocks++;
- return;
- }
- }
- }

- if (ext2fs_has_group_desc_csum(fs) &&
- (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
- /*
- * If the block bitmap is uninitialized, which means
- * nothing other than standard metadata in use.
- */
- return;
- } else if (ext2fs_test_block_bitmap2(rfs->old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk)) {
- ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
- rfs->needed_blocks++;
- }
+ return retval;
}

-
/*
- * This routine marks and unmarks reserved blocks in the new block
- * bitmap. It also determines which blocks need to be moved and
- * places this information into the move_blocks bitmap.
+ * This routine is shared by the online and offline resize routines.
+ * All of the information which is adjusted in memory is done here.
*/
-static errcode_t blocks_to_move(ext2_resize_t rfs)
+errcode_t adjust_fs_info(ext2_filsys fs, ext2_filsys old_fs, blk64_t new_size)
{
- int j, has_super;
- dgrp_t i, max_groups, g;
- blk64_t blk, group_blk;
- blk64_t old_blocks, new_blocks, group_end, cluster_freed;
- blk64_t new_size;
- unsigned int meta_bg, meta_bg_size;
errcode_t retval;
- ext2_filsys fs, old_fs;
- ext2fs_block_bitmap meta_bmap, new_meta_bmap = NULL;
- int flex_bg;
-
- fs = rfs->new_fs;
- old_fs = rfs->old_fs;
- if (ext2fs_blocks_count(old_fs->super) > ext2fs_blocks_count(fs->super))
- fs = rfs->old_fs;
-
- retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
- &rfs->move_blocks);
- if (retval)
- return retval;
-
- retval = ext2fs_allocate_block_bitmap(fs, _("meta-data blocks"),
- &meta_bmap);
- if (retval)
- return retval;
-
- retval = mark_table_blocks(old_fs, meta_bmap);
- if (retval)
- return retval;
-
- fs = rfs->new_fs;
-
- /*
- * If we're shrinking the filesystem, we need to move any
- * group's metadata blocks (either allocation bitmaps or the
- * inode table) which are beyond the end of the new
- * filesystem.
- */
- new_size = ext2fs_blocks_count(fs->super);
- if (new_size < ext2fs_blocks_count(old_fs->super)) {
- for (g = 0; g < fs->group_desc_count; g++) {
- int realloc = 0;
- /*
- * ext2fs_allocate_group_table will re-allocate any
- * metadata blocks whose location is set to zero.
- */
- if (ext2fs_block_bitmap_loc(fs, g) >= new_size) {
- ext2fs_block_bitmap_loc_set(fs, g, 0);
- realloc = 1;
- }
- if (ext2fs_inode_bitmap_loc(fs, g) >= new_size) {
- ext2fs_inode_bitmap_loc_set(fs, g, 0);
- realloc = 1;
- }
- if ((ext2fs_inode_table_loc(fs, g) +
- fs->inode_blocks_per_group) > new_size) {
- ext2fs_inode_table_loc_set(fs, g, 0);
- realloc = 1;
- }
-
- if (realloc) {
- retval = ext2fs_allocate_group_table(fs, g, 0);
- if (retval)
- return retval;
- }
- }
- }
-
- /*
- * If we're shrinking the filesystem, we need to move all of
- * the blocks that don't fit any more
- */
- for (blk = ext2fs_blocks_count(fs->super);
- blk < ext2fs_blocks_count(old_fs->super); blk++) {
- g = ext2fs_group_of_blk2(fs, blk);
- if (ext2fs_has_group_desc_csum(fs) &&
- ext2fs_bg_flags_test(old_fs, g, EXT2_BG_BLOCK_UNINIT)) {
- /*
- * The block bitmap is uninitialized, so skip
- * to the next block group.
- */
- blk = ext2fs_group_first_block2(fs, g+1) - 1;
- continue;
- }
- if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk)) {
- ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
- rfs->needed_blocks++;
- }
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
- }
-
- if (old_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- old_blocks = old_fs->super->s_first_meta_bg;
- else
- old_blocks = old_fs->desc_blocks +
- old_fs->super->s_reserved_gdt_blocks;
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- new_blocks = fs->super->s_first_meta_bg;
- else
- new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
-
- retval = reserve_sparse_super2_last_group(rfs, meta_bmap);
- if (retval)
- goto errout;
+ blk64_t blk, group_block;
+ blk64_t real_end;
+ unsigned long i;
+ double percent;

- if (old_blocks == new_blocks) {
- retval = 0;
- goto errout;
- }
+ ext2fs_blocks_count_set(fs->super, new_size);
+ fs->group_desc_count = ext2fs_div64_ceil(new_size -
+ fs->super->s_first_data_block,
+ EXT2_BLOCKS_PER_GROUP(fs->super));
+ fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
+ EXT2_DESC_PER_BLOCK(fs->super));
+ fs->super->s_inodes_count = fs->super->s_inodes_per_group *
+ fs->group_desc_count;

- max_groups = fs->group_desc_count;
- if (max_groups > old_fs->group_desc_count)
- max_groups = old_fs->group_desc_count;
- group_blk = old_fs->super->s_first_data_block;
/*
- * If we're reducing the number of descriptor blocks, this
- * makes life easy. :-) We just have to mark some extra
- * blocks as free.
+ * Don't update free blocks count. That gets recalculated when we are
+ * done.
*/
- if (old_blocks > new_blocks) {
- if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
- retval = ext2fs_allocate_block_bitmap(fs,
- _("new meta blocks"),
- &new_meta_bmap);
- if (retval)
- goto errout;
-
- retval = mark_table_blocks(fs, new_meta_bmap);
- if (retval)
- goto errout;
- }
-
- for (i = 0; i < max_groups; i++) {
- if (!ext2fs_bg_has_super(old_fs, i)) {
- group_blk += fs->super->s_blocks_per_group;
- continue;
- }
- group_end = group_blk + 1 + old_blocks;
- for (blk = group_blk + 1 + new_blocks;
- blk < group_end;) {
- if (new_meta_bmap == NULL ||
- !ext2fs_test_block_bitmap2(new_meta_bmap,
- blk)) {
- cluster_freed =
- EXT2FS_CLUSTER_RATIO(fs) -
- (blk &
- EXT2FS_CLUSTER_MASK(fs));
- if (cluster_freed > group_end - blk)
- cluster_freed = group_end - blk;
- ext2fs_block_alloc_stats2(fs, blk, -1);
- blk += EXT2FS_CLUSTER_RATIO(fs);
- rfs->needed_blocks -= cluster_freed;
- continue;
- }
- rfs->needed_blocks--;
- blk++;
- }
- group_blk += fs->super->s_blocks_per_group;
- }
- retval = 0;
- goto errout;
- }
- /*
- * If we're increasing the number of descriptor blocks, life
- * gets interesting....
- */
- meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
- flex_bg = fs->super->s_feature_incompat &
- EXT4_FEATURE_INCOMPAT_FLEX_BG;
- /* first reserve all of the existing fs meta blocks */
- for (i = 0; i < max_groups; i++) {
- has_super = ext2fs_bg_has_super(fs, i);
- if (has_super)
- mark_fs_metablock(rfs, meta_bmap, i, group_blk);
-
- meta_bg = i / meta_bg_size;
- if (!(fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG) ||
- (meta_bg < fs->super->s_first_meta_bg)) {
- if (has_super) {
- for (blk = group_blk+1;
- blk < group_blk + 1 + new_blocks; blk++)
- mark_fs_metablock(rfs, meta_bmap,
- i, blk);
- }
- } else {
- if (has_super)
- has_super = 1;
- if (((i % meta_bg_size) == 0) ||
- ((i % meta_bg_size) == 1) ||
- ((i % meta_bg_size) == (meta_bg_size-1)))
- mark_fs_metablock(rfs, meta_bmap, i,
- group_blk + has_super);
- }
-
- /*
- * Reserve the existing meta blocks that we know
- * aren't to be moved.
- *
- * For flex_bg file systems, in order to avoid
- * overwriting fs metadata (especially inode table
- * blocks) belonging to a different block group when
- * we are relocating the inode tables, we need to
- * reserve all existing fs metadata blocks.
- */
- if (ext2fs_block_bitmap_loc(fs, i))
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks,
- ext2fs_block_bitmap_loc(fs, i));
- else if (flex_bg && i < old_fs->group_desc_count)
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks,
- ext2fs_block_bitmap_loc(old_fs, i));
-
- if (ext2fs_inode_bitmap_loc(fs, i))
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks,
- ext2fs_inode_bitmap_loc(fs, i));
- else if (flex_bg && i < old_fs->group_desc_count)
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks,
- ext2fs_inode_bitmap_loc(old_fs, i));
-
- if (ext2fs_inode_table_loc(fs, i))
- ext2fs_mark_block_bitmap_range2(rfs->reserve_blocks,
- ext2fs_inode_table_loc(fs, i),
- fs->inode_blocks_per_group);
- else if (flex_bg && i < old_fs->group_desc_count)
- ext2fs_mark_block_bitmap_range2(rfs->reserve_blocks,
- ext2fs_inode_table_loc(old_fs, i),
- old_fs->inode_blocks_per_group);
-
- group_blk += rfs->new_fs->super->s_blocks_per_group;
- }
-
- /* Allocate the missing data structures */
- for (i = 0; i < max_groups; i++) {
- if (ext2fs_inode_table_loc(fs, i) &&
- ext2fs_inode_bitmap_loc(fs, i) &&
- ext2fs_block_bitmap_loc(fs, i))
- continue;
-
- retval = ext2fs_allocate_group_table2(fs, i,
- rfs->reserve_blocks,
- EXT2FS_ALLOC_TABLE_UPDATE_STATS |
- EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP);
- if (retval)
- goto errout;
-
- /*
- * For those structures that have changed, we need to
- * do bookkeepping.
- */
- if (ext2fs_block_bitmap_loc(old_fs, i) !=
- (blk = ext2fs_block_bitmap_loc(fs, i))) {
- if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk))
- ext2fs_mark_block_bitmap2(rfs->move_blocks,
- blk);
- }
- if (ext2fs_inode_bitmap_loc(old_fs, i) !=
- (blk = ext2fs_inode_bitmap_loc(fs, i))) {
- if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk))
- ext2fs_mark_block_bitmap2(rfs->move_blocks,
- blk);
- }
-
- /*
- * The inode table, if we need to relocate it, is
- * handled specially. We have to reserve the blocks
- * for both the old and the new inode table, since we
- * can't have the inode table be destroyed during the
- * block relocation phase.
- */
- if (ext2fs_inode_table_loc(fs, i) == ext2fs_inode_table_loc(old_fs, i))
- continue; /* inode table not moved */
-
- rfs->needed_blocks += fs->inode_blocks_per_group;
-
- /*
- * Mark the new inode table as in use in the new block
- * allocation bitmap, and move any blocks that might
- * be necessary.
- */
- for (blk = ext2fs_inode_table_loc(fs, i), j=0;
- j < fs->inode_blocks_per_group ; j++, blk++) {
- if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk))
- ext2fs_mark_block_bitmap2(rfs->move_blocks,
- blk);
- }
-
- /*
- * Make sure the old inode table is reserved in the
- * block reservation bitmap.
- */
- for (blk = ext2fs_inode_table_loc(rfs->old_fs, i), j=0;
- j < fs->inode_blocks_per_group ; j++, blk++)
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
- }
- retval = 0;
-
-errout:
- if (new_meta_bmap)
- ext2fs_free_block_bitmap(new_meta_bmap);
- if (meta_bmap)
- ext2fs_free_block_bitmap(meta_bmap);
-
- return retval;
-}
-
-/*
- * This helper function tries to allocate a new block. We try to
- * avoid hitting the original group descriptor blocks at least at
- * first, since we want to make it possible to recover from a badly
- * aborted resize operation as much as possible.
- *
- * In the future, I may further modify this routine to balance out
- * where we get the new blocks across the various block groups.
- * Ideally we would allocate blocks that corresponded with the block
- * group of the containing inode, and keep contiguous blocks
- * together. However, this very difficult to do efficiently, since we
- * don't have the necessary information up front.
- */
-
-#define AVOID_OLD 1
-#define DESPERATION 2
-
-static void init_block_alloc(ext2_resize_t rfs)
-{
- rfs->alloc_state = AVOID_OLD;
- rfs->new_blk = rfs->new_fs->super->s_first_data_block;
-#if 0
- /* HACK for testing */
- if (ext2fs_blocks_count(rfs->new_fs->super) >
- ext2fs_blocks_count(rfs->old_fs->super))
- rfs->new_blk = ext2fs_blocks_count(rfs->old_fs->super);
-#endif
-}
-
-static blk64_t get_new_block(ext2_resize_t rfs)
-{
- ext2_filsys fs = rfs->new_fs;
-
- while (1) {
- if (rfs->new_blk >= ext2fs_blocks_count(fs->super)) {
- if (rfs->alloc_state == DESPERATION)
- return 0;
-
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_BMOVE)
- printf("Going into desperation mode "
- "for block allocations\n");
-#endif
- rfs->alloc_state = DESPERATION;
- rfs->new_blk = fs->super->s_first_data_block;
- continue;
- }
- if (ext2fs_test_block_bitmap2(fs->block_map, rfs->new_blk) ||
- ext2fs_test_block_bitmap2(rfs->reserve_blocks,
- rfs->new_blk) ||
- ((rfs->alloc_state == AVOID_OLD) &&
- (rfs->new_blk < ext2fs_blocks_count(rfs->old_fs->super)) &&
- ext2fs_test_block_bitmap2(rfs->old_fs->block_map,
- rfs->new_blk))) {
- rfs->new_blk++;
- continue;
- }
- return rfs->new_blk;
- }
-}
-
-static errcode_t resize2fs_get_alloc_block(ext2_filsys fs, blk64_t goal,
- blk64_t *ret)
-{
- ext2_resize_t rfs = (ext2_resize_t) fs->priv_data;
- blk64_t blk;
-
- blk = get_new_block(rfs);
- if (!blk)
- return ENOSPC;
-
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & 0xF)
- printf("get_alloc_block allocating %llu\n", blk);
-#endif
-
- ext2fs_mark_block_bitmap2(rfs->old_fs->block_map, blk);
- ext2fs_mark_block_bitmap2(rfs->new_fs->block_map, blk);
- *ret = (blk64_t) blk;
- return 0;
-}
-
-static errcode_t block_mover(ext2_resize_t rfs)
-{
- blk64_t blk, old_blk, new_blk;
- ext2_filsys fs = rfs->new_fs;
- ext2_filsys old_fs = rfs->old_fs;
- errcode_t retval;
- __u64 size;
- int c;
- int to_move, moved;
- ext2_badblocks_list badblock_list = 0;
- int bb_modified = 0;
-
- fs->get_alloc_block = resize2fs_get_alloc_block;
- old_fs->get_alloc_block = resize2fs_get_alloc_block;
-
- retval = ext2fs_read_bb_inode(old_fs, &badblock_list);
- if (retval)
- return retval;
-
- new_blk = fs->super->s_first_data_block;
- if (!rfs->itable_buf) {
- retval = ext2fs_get_array(fs->blocksize,
- fs->inode_blocks_per_group,
- &rfs->itable_buf);
- if (retval)
- return retval;
- }
- retval = ext2fs_create_extent_table(&rfs->bmap, 0);
- if (retval)
- return retval;

/*
- * The first step is to figure out where all of the blocks
- * will go.
+ * Adjust the number of reserved blocks
*/
- to_move = moved = 0;
- init_block_alloc(rfs);
- for (blk = B2C(old_fs->super->s_first_data_block);
- blk < ext2fs_blocks_count(old_fs->super);
- blk += EXT2FS_CLUSTER_RATIO(fs)) {
- if (!ext2fs_test_block_bitmap2(old_fs->block_map, blk))
- continue;
- if (!ext2fs_test_block_bitmap2(rfs->move_blocks, blk))
- continue;
- if (ext2fs_badblocks_list_test(badblock_list, blk)) {
- ext2fs_badblocks_list_del(badblock_list, blk);
- bb_modified++;
- continue;
- }
-
- new_blk = get_new_block(rfs);
- if (!new_blk) {
- retval = ENOSPC;
- goto errout;
- }
- ext2fs_block_alloc_stats2(fs, new_blk, +1);
- ext2fs_add_extent_entry(rfs->bmap, B2C(blk), B2C(new_blk));
- to_move++;
- }
-
- if (to_move == 0) {
- if (rfs->bmap) {
- ext2fs_free_extent_table(rfs->bmap);
- rfs->bmap = 0;
- }
- retval = 0;
- goto errout;
- }
+ percent = (ext2fs_r_blocks_count(old_fs->super) * 100.0) /
+ ext2fs_blocks_count(old_fs->super);
+ ext2fs_r_blocks_count_set(fs->super,
+ (percent * ext2fs_blocks_count(fs->super) /
+ 100.0));

/*
- * Step two is to actually move the blocks
+ * Adjust the bitmaps for size
*/
- retval = ext2fs_iterate_extent(rfs->bmap, 0, 0, 0);
+ retval = ext2fs_resize_inode_bitmap2(fs->super->s_inodes_count,
+ fs->super->s_inodes_count,
+ fs->inode_map);
if (retval) goto errout;

- if (rfs->progress) {
- retval = (rfs->progress)(rfs, E2_RSZ_BLOCK_RELOC_PASS,
- 0, to_move);
- if (retval)
- goto errout;
- }
- while (1) {
- retval = ext2fs_iterate_extent(rfs->bmap, &old_blk, &new_blk, &size);
- if (retval) goto errout;
- if (!size)
- break;
- old_blk = C2B(old_blk);
- new_blk = C2B(new_blk);
- size = C2B(size);
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_BMOVE)
- printf("Moving %llu blocks %llu->%llu\n",
- size, old_blk, new_blk);
-#endif
- do {
- c = size;
- if (c > fs->inode_blocks_per_group)
- c = fs->inode_blocks_per_group;
- retval = io_channel_read_blk64(fs->io, old_blk, c,
- rfs->itable_buf);
- if (retval) goto errout;
- retval = io_channel_write_blk64(fs->io, new_blk, c,
- rfs->itable_buf);
- if (retval) goto errout;
- size -= c;
- new_blk += c;
- old_blk += c;
- moved += c;
- if (rfs->progress) {
- io_channel_flush(fs->io);
- retval = (rfs->progress)(rfs,
- E2_RSZ_BLOCK_RELOC_PASS,
- moved, to_move);
- if (retval)
- goto errout;
- }
- } while (size > 0);
- io_channel_flush(fs->io);
- }
-
-errout:
- if (badblock_list) {
- if (!retval && bb_modified)
- retval = ext2fs_update_bb_inode(old_fs,
- badblock_list);
- ext2fs_badblocks_list_free(badblock_list);
- }
- return retval;
-}
-
-
-/* --------------------------------------------------------------------
- *
- * Resize processing, phase 3
- *
- * --------------------------------------------------------------------
- */
-
-
-/*
- * The extent translation table is stored in clusters so we need to
- * take special care when mapping a source block number to its
- * destination block number.
- */
-static __u64 extent_translate(ext2_filsys fs, ext2_extent extent, __u64 old_loc)
-{
- __u64 new_block = C2B(ext2fs_extent_translate(extent, B2C(old_loc)));
-
- if (new_block != 0)
- new_block += old_loc & (EXT2FS_CLUSTER_RATIO(fs) - 1);
- return new_block;
-}
-
-struct process_block_struct {
- ext2_resize_t rfs;
- ext2_ino_t ino;
- ext2_ino_t old_ino;
- struct ext2_inode * inode;
- errcode_t error;
- int is_dir;
- int changed;
- int has_extents;
-};
-
-static int process_block(ext2_filsys fs, blk64_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk64_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)), void *priv_data)
-{
- struct process_block_struct *pb;
- errcode_t retval;
- blk64_t block, new_block;
- int ret = 0;
-
- pb = (struct process_block_struct *) priv_data;
- block = *block_nr;
- if (pb->rfs->bmap) {
- new_block = extent_translate(fs, pb->rfs->bmap, block);
- if (new_block) {
- *block_nr = new_block;
- ret |= BLOCK_CHANGED;
- pb->changed = 1;
-#ifdef RESIZE2FS_DEBUG
- if (pb->rfs->flags & RESIZE_DEBUG_BMOVE)
- printf("ino=%u, blockcnt=%lld, %llu->%llu\n",
- pb->old_ino, blockcnt, block,
- new_block);
-#endif
- block = new_block;
- }
- }
-
- if (pb->is_dir) {
- retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
- block, (int) blockcnt);
- if (retval) {
- pb->error = retval;
- ret |= BLOCK_ABORT;
- }
- }
- return ret;
-}
-
-/*
- * Progress callback
- */
-static errcode_t progress_callback(ext2_filsys fs,
- ext2_inode_scan scan EXT2FS_ATTR((unused)),
- dgrp_t group, void * priv_data)
-{
- ext2_resize_t rfs = (ext2_resize_t) priv_data;
- errcode_t retval;
+ real_end = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count) - 1 +
+ fs->super->s_first_data_block;
+ retval = ext2fs_resize_block_bitmap2(new_size - 1,
+ real_end, fs->block_map);
+ if (retval) goto errout;

/*
- * This check is to protect against old ext2 libraries. It
- * shouldn't be needed against new libraries.
+ * Reallocate the group descriptors as necessary.
*/
- if ((group+1) == 0)
- return 0;
-
- if (rfs->progress) {
- io_channel_flush(fs->io);
- retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
- group+1, fs->group_desc_count);
- if (retval)
- return retval;
- }
-
- return 0;
-}
-
-static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
- struct ext2_inode *inode, int *changed)
-{
- char *buf = NULL;
- blk64_t new_block;
- errcode_t err = 0;
-
- /* No EA block or no remapping? Quit early. */
- if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 && !rfs->bmap)
- return 0;
- new_block = extent_translate(rfs->old_fs, rfs->bmap,
- ext2fs_file_acl_block(rfs->old_fs, inode));
- if (new_block == 0)
- return 0;
-
- /* Set the new ACL block */
- ext2fs_file_acl_block_set(rfs->old_fs, inode, new_block);
-
- /* Update checksum */
- if (EXT2_HAS_RO_COMPAT_FEATURE(rfs->new_fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
- err = ext2fs_get_mem(rfs->old_fs->blocksize, &buf);
- if (err)
- return err;
- rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- err = ext2fs_read_ext_attr3(rfs->old_fs, new_block, buf, ino);
- rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
- if (err)
- goto out;
- err = ext2fs_write_ext_attr3(rfs->old_fs, new_block, buf, ino);
- if (err)
- goto out;
- }
- *changed = 1;
-
-out:
- ext2fs_free_mem(&buf);
- return err;
-}
-
-/* Rewrite extents */
-static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino)
-{
- ext2_extent_handle_t handle;
- struct ext2fs_extent extent;
- errcode_t errcode;
- struct ext2_extent_info info;
-
- 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;
-}
-
-static void quiet_com_err_proc(const char *whoami, errcode_t code,
- const char *fmt, va_list args)
-{
-}
-
-static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
-{
- struct process_block_struct pb;
- ext2_ino_t ino, new_inode;
- struct ext2_inode *inode = NULL;
- ext2_inode_scan scan = NULL;
- errcode_t retval;
- char *block_buf = 0;
- ext2_ino_t start_to_move;
- int inode_size;
-
- if ((rfs->old_fs->group_desc_count <=
- rfs->new_fs->group_desc_count) &&
- !rfs->bmap)
- return 0;
-
- set_com_err_hook(quiet_com_err_proc);
-
- retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
- if (retval) goto errout;
-
- retval = ext2fs_init_dblist(rfs->old_fs, 0);
- if (retval) goto errout;
- retval = ext2fs_get_array(rfs->old_fs->blocksize, 3, &block_buf);
- if (retval) goto errout;
-
- start_to_move = (rfs->new_fs->group_desc_count *
- rfs->new_fs->super->s_inodes_per_group);
-
- if (rfs->progress) {
- retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
- 0, rfs->old_fs->group_desc_count);
+ if (old_fs->desc_blocks != fs->desc_blocks) {
+ retval = ext2fs_resize_mem(old_fs->desc_blocks *
+ fs->blocksize,
+ fs->desc_blocks * fs->blocksize,
+ &fs->group_desc);
if (retval)
goto errout;
+ if (fs->desc_blocks > old_fs->desc_blocks)
+ memset((char *) fs->group_desc +
+ (old_fs->desc_blocks * fs->blocksize), 0,
+ (fs->desc_blocks - old_fs->desc_blocks) *
+ fs->blocksize);
}
- ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs);
- pb.rfs = rfs;
- pb.inode = inode;
- pb.error = 0;
- new_inode = EXT2_FIRST_INODE(rfs->new_fs->super);
- inode_size = EXT2_INODE_SIZE(rfs->new_fs->super);
- inode = malloc(inode_size);
- if (!inode) {
- retval = ENOMEM;
- goto errout;
- }
+
/*
- * First, copy all of the inodes that need to be moved
- * elsewhere in the inode table
+ * If the resize_inode feature is set, and we are changing the
+ * number of descriptor blocks, then adjust
+ * s_reserved_gdt_blocks if possible to avoid needing to move
+ * the inode table either now or in the future.
*/
- while (1) {
- retval = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size);
- if (retval) goto errout;
- if (!ino)
- break;
-
- if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
- continue; /* inode not in use */
-
- pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
- pb.changed = 0;
-
- /* Remap EA block */
- retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
- if (retval)
- goto errout;
-
- new_inode = ino;
- if (ino <= start_to_move)
- goto remap_blocks; /* Don't need to move inode. */
-
- /*
- * Find a new inode. Now that extents and directory blocks
- * are tied to the inode number through the checksum, we must
- * set up the new inode before we start rewriting blocks.
- */
- retval = ext2fs_new_inode(rfs->new_fs, 0, 0, 0, &new_inode);
- if (retval)
- goto errout;
-
- ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
- pb.is_dir);
- inode->i_ctime = time(0);
- retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
- inode, inode_size);
- if (retval)
- goto errout;
- pb.changed = 0;
-
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_INODEMAP)
- printf("Inode moved %u->%u\n", ino, new_inode);
-#endif
- if (!rfs->imap) {
- retval = ext2fs_create_extent_table(&rfs->imap, 0);
- if (retval)
- goto errout;
- }
- ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
+ adjust_reserved_gdt_blocks(old_fs, fs);

-remap_blocks:
- if (pb.changed)
- retval = ext2fs_write_inode_full(rfs->old_fs,
- new_inode,
- inode, inode_size);
- if (retval)
- goto errout;
+ if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+ (fs->super->s_first_meta_bg > fs->desc_blocks)) {
+ fs->super->s_feature_incompat &=
+ ~EXT2_FEATURE_INCOMPAT_META_BG;
+ fs->super->s_first_meta_bg = 0;
+ }

- /* Rewrite extent block checksums with new inode number */
- if (EXT2_HAS_RO_COMPAT_FEATURE(rfs->old_fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
- (inode->i_flags & EXT4_EXTENTS_FL)) {
- rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- retval = rewrite_extents(rfs->old_fs, new_inode);
- rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
- if (retval)
- goto errout;
- }
+ /*
+ * Update the location of the backup superblocks if the
+ * sparse_super2 feature is enabled.
+ */
+ if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
+ dgrp_t last_bg = fs->group_desc_count - 1;
+ dgrp_t old_last_bg = old_fs->group_desc_count - 1;

- /*
- * Update inodes to point to new blocks; schedule directory
- * blocks for inode remapping. Need to write out dir blocks
- * with new inode numbers if we have metadata_csum enabled.
- */
- if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
- (rfs->bmap || pb.is_dir)) {
- pb.ino = new_inode;
- pb.old_ino = ino;
- pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
- rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- retval = ext2fs_block_iterate3(rfs->old_fs,
- new_inode, 0, block_buf,
- process_block, &pb);
- rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
- if (retval)
- goto errout;
- if (pb.error) {
- retval = pb.error;
- goto errout;
- }
- } else if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
- (rfs->bmap || pb.is_dir)) {
- /* inline data dir; update it too */
- retval = ext2fs_add_dir_block2(rfs->old_fs->dblist,
- new_inode, 0, 0);
- if (retval)
- goto errout;
+ /* For shrinking we already did this in prepare_shrink() */
+ if (last_bg > old_last_bg) {
+ if (old_fs->group_desc_count == 1)
+ fs->super->s_backup_bgs[0] = 1;
+ if (old_fs->group_desc_count == 1 &&
+ fs->super->s_backup_bgs[0])
+ fs->super->s_backup_bgs[0] = last_bg;
+ else if (fs->super->s_backup_bgs[1])
+ fs->super->s_backup_bgs[1] = last_bg;
}
}
- io_channel_flush(rfs->old_fs->io);

errout:
- reset_com_err_hook();
- if (rfs->bmap) {
- ext2fs_free_extent_table(rfs->bmap);
- rfs->bmap = 0;
- }
- if (scan)
- ext2fs_close_inode_scan(scan);
- if (block_buf)
- ext2fs_free_mem(&block_buf);
- free(inode);
return retval;
}

-/* --------------------------------------------------------------------
- *
- * Resize processing, phase 4.
- *
- * --------------------------------------------------------------------
+/*
+ * This helper function creates a block bitmap with all of the
+ * filesystem meta-data blocks.
*/
-
-struct istruct {
- ext2_resize_t rfs;
- errcode_t err;
- unsigned int max_dirs;
- unsigned int num;
-};
-
-static int check_and_change_inodes(ext2_ino_t dir,
- int entry EXT2FS_ATTR((unused)),
- struct ext2_dir_entry *dirent, int offset,
- int blocksize EXT2FS_ATTR((unused)),
- char *buf EXT2FS_ATTR((unused)),
- void *priv_data)
+static errcode_t mark_table_blocks(ext2_filsys fs,
+ ext2fs_block_bitmap bmap)
{
- struct istruct *is = (struct istruct *) priv_data;
- struct ext2_inode inode;
- ext2_ino_t new_inode;
- errcode_t retval;
- int ret = 0;
-
- if (is->rfs->progress && offset == 0) {
- io_channel_flush(is->rfs->old_fs->io);
- is->err = (is->rfs->progress)(is->rfs,
- E2_RSZ_INODE_REF_UPD_PASS,
- ++is->num, is->max_dirs);
- if (is->err)
- return DIRENT_ABORT;
- }
-
- /*
- * If we have checksums enabled and the inode wasn't present in the
- * old fs, then we must rewrite all dir blocks with new checksums.
- */
- if (EXT2_HAS_RO_COMPAT_FEATURE(is->rfs->old_fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
- !ext2fs_test_inode_bitmap2(is->rfs->old_fs->inode_map, dir))
- ret |= DIRENT_CHANGED;
-
- if (!dirent->inode)
- return ret;
+ dgrp_t i;
+ blk64_t blk;

- new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
+ for (i = 0; i < fs->group_desc_count; i++) {
+ ext2fs_reserve_super_and_bgd(fs, i, bmap);

- if (!new_inode)
- return ret;
-#ifdef RESIZE2FS_DEBUG
- if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
- printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
- dir, ext2fs_dirent_name_len(dirent), dirent->name,
- dirent->inode, new_inode);
-#endif
+ /*
+ * Mark the blocks used for the inode table
+ */
+ blk = ext2fs_inode_table_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap_range2(bmap, blk,
+ fs->inode_blocks_per_group);

- dirent->inode = new_inode;
+ /*
+ * Mark block used for the block bitmap
+ */
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);

- /* Update the directory mtime and ctime */
- retval = ext2fs_read_inode(is->rfs->old_fs, dir, &inode);
- if (retval == 0) {
- inode.i_mtime = inode.i_ctime = time(0);
- is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
- if (is->err)
- return ret | DIRENT_ABORT;
+ /*
+ * Mark block used for the inode bitmap
+ */
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);
}
-
- return ret | DIRENT_CHANGED;
+ return 0;
}

-static errcode_t inode_ref_fix(ext2_resize_t rfs)
+/*
+ * Free group descriptor blocks that aren't needed anymore when fs is shrunk
+ */
+static errcode_t free_gd_blocks(ext2_resize_t rfs)
{
- errcode_t retval;
- struct istruct is;
+ errcode_t retval = 0;
+ ext2_filsys fs = rfs->new_fs, old_fs = rfs->old_fs;
+ blk64_t old_blocks, new_blocks, group_end, cluster_freed;
+ ext2fs_block_bitmap new_meta_bmap = NULL;
+ blk64_t blk, group_blk;
+ dgrp_t i;

- if (!rfs->imap)
+ if (old_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ old_blocks = old_fs->super->s_first_meta_bg;
+ else
+ old_blocks = old_fs->desc_blocks +
+ old_fs->super->s_reserved_gdt_blocks;
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ new_blocks = fs->super->s_first_meta_bg;
+ else
+ new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+ if (old_blocks == new_blocks)
return 0;

- /*
- * Now, we iterate over all of the directories to update the
- * inode references
- */
- is.num = 0;
- is.max_dirs = ext2fs_dblist_count2(rfs->old_fs->dblist);
- is.rfs = rfs;
- is.err = 0;
+ if (new_blocks > old_blocks) {
+ fprintf(stderr, "%s: Called when growing filesystem (%llu > "
+ "%llu)!\n", __func__, (unsigned long long)new_blocks,
+ (unsigned long long)old_blocks);
+ exit(1);
+ }

- if (rfs->progress) {
- retval = (rfs->progress)(rfs, E2_RSZ_INODE_REF_UPD_PASS,
- 0, is.max_dirs);
+ if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ retval = ext2fs_allocate_block_bitmap(fs,
+ _("new meta blocks"),
+ &new_meta_bmap);
if (retval)
goto errout;
- }

- rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
- DIRENT_FLAG_INCLUDE_EMPTY, 0,
- check_and_change_inodes, &is);
- rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
- if (retval)
- goto errout;
- if (is.err) {
- retval = is.err;
- goto errout;
+ retval = mark_table_blocks(fs, new_meta_bmap);
+ if (retval)
+ goto errout;
}

- if (rfs->progress && (is.num < is.max_dirs))
- (rfs->progress)(rfs, E2_RSZ_INODE_REF_UPD_PASS,
- is.max_dirs, is.max_dirs);
-
+ group_blk = fs->super->s_first_data_block;
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (!ext2fs_bg_has_super(fs, i)) {
+ group_blk += fs->super->s_blocks_per_group;
+ continue;
+ }
+ group_end = group_blk + 1 + old_blocks;
+ for (blk = group_blk + 1 + new_blocks; blk < group_end;) {
+ if (new_meta_bmap == NULL ||
+ !ext2fs_test_block_bitmap2(new_meta_bmap,
+ blk)) {
+ cluster_freed =
+ EXT2FS_CLUSTER_RATIO(fs) -
+ (blk &
+ EXT2FS_CLUSTER_MASK(fs));
+ if (cluster_freed > group_end - blk)
+ cluster_freed = group_end - blk;
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+ blk += EXT2FS_CLUSTER_RATIO(fs);
+ continue;
+ }
+ blk++;
+ }
+ group_blk += fs->super->s_blocks_per_group;
+ }
errout:
- ext2fs_free_extent_table(rfs->imap);
- rfs->imap = 0;
return retval;
}

-
-/* --------------------------------------------------------------------
- *
- * Resize processing, phase 5.
- *
- * In this phase we actually move the inode table around, and then
- * update the summary statistics. This is scary, since aborting here
- * will potentially scramble the filesystem. (We are moving the
- * inode tables around in place, and so the potential for lost data,
- * or at the very least scrambling the mapping between filenames and
- * inode numbers is very high in case of a power failure here.)
- * --------------------------------------------------------------------
- */
-
-
/*
- * A very scary routine --- this one moves the inode table around!!!
- *
- * After this you have to use the rfs->new_fs file handle to read and
- * write inodes.
+ * Identify blocks that need moving to make space for new group descriptors,
+ * move them, and mark them as used.
*/
-static errcode_t move_itables(ext2_resize_t rfs)
+static errcode_t make_space_for_gd(ext2_resize_t rfs)
{
- int n, num, size;
- long long diff;
- dgrp_t i, max_groups;
- ext2_filsys fs = rfs->new_fs;
- char *cp;
- blk64_t old_blk, new_blk, blk, cluster_freed;
+ int j, has_super;
+ dgrp_t i;
+ blk64_t blk, group_blk;
+ blk64_t old_blocks, new_blocks;
+ unsigned int meta_bg, meta_bg_size;
errcode_t retval;
- int j, to_move, moved;
- ext2fs_block_bitmap new_bmap = NULL;
+ ext2_filsys fs = rfs->new_fs, old_fs = rfs->old_fs;
+ ext2fs_block_bitmap move_blocks;
+
+ retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
+ &move_blocks);
+ if (retval)
+ return retval;

- max_groups = fs->group_desc_count;
- if (max_groups > rfs->old_fs->group_desc_count)
- max_groups = rfs->old_fs->group_desc_count;
+ if (old_fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ old_blocks = old_fs->super->s_first_meta_bg;
+ else
+ old_blocks = old_fs->desc_blocks +
+ old_fs->super->s_reserved_gdt_blocks;
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ new_blocks = fs->super->s_first_meta_bg;
+ else
+ new_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;

- size = fs->blocksize * fs->inode_blocks_per_group;
- if (!rfs->itable_buf) {
- retval = ext2fs_get_mem(size, &rfs->itable_buf);
- if (retval)
- return retval;
+ if (old_blocks == new_blocks) {
+ retval = 0;
+ goto errout;
}

- if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
- retval = ext2fs_allocate_block_bitmap(fs, _("new meta blocks"),
- &new_bmap);
- if (retval)
- return retval;
-
- retval = mark_table_blocks(fs, new_bmap);
- if (retval)
- goto errout;
+ if (new_blocks < old_blocks) {
+ fprintf(stderr, "%s: Called when shrinking filesystem (%llu < "
+ "%llu)!\n", __func__, (unsigned long long)new_blocks,
+ (unsigned long long)old_blocks);
+ exit(1);
}

+ meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super);
/*
- * Figure out how many inode tables we need to move
+ * Mark space for new descriptor blocks as needing moving. We need to
+ * handle only groups with old style group descriptor blocks - for
+ * groups using META_BG the number of group descriptor blocks doesn't
+ * change.
*/
- to_move = moved = 0;
- for (i=0; i < max_groups; i++)
- if (ext2fs_inode_table_loc(rfs->old_fs, i) !=
- ext2fs_inode_table_loc(fs, i))
- to_move++;
+ group_blk = old_fs->super->s_first_data_block;
+ for (i = 0; i < old_fs->group_desc_count;
+ i++, group_blk += fs->super->s_blocks_per_group) {
+ if (!ext2fs_bg_has_super(fs, i))
+ continue;

- if (to_move == 0) {
- retval = 0;
- goto errout;
- }
+ meta_bg = i / meta_bg_size;
+ if (fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_META_BG &&
+ meta_bg >= fs->super->s_first_meta_bg)
+ break;

- if (rfs->progress) {
- retval = rfs->progress(rfs, E2_RSZ_MOVE_ITABLE_PASS,
- 0, to_move);
- if (retval)
- goto errout;
+ ext2fs_mark_block_bitmap_range2(move_blocks,
+ group_blk + old_blocks + 1,
+ new_blocks - old_blocks);
}

- rfs->old_fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
-
- for (i=0; i < max_groups; i++) {
- old_blk = ext2fs_inode_table_loc(rfs->old_fs, i);
- new_blk = ext2fs_inode_table_loc(fs, i);
- diff = new_blk - old_blk;
-
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE)
- printf("Itable move group %d block %llu->%llu (diff %lld)\n",
- i, old_blk, new_blk, diff);
-#endif
+ retval = ext2fs_move_blocks(fs, move_blocks, NULL);
+ if (retval)
+ goto errout;

- if (!diff)
+ /* Blocks for group descriptors are unused now, mark them as used */
+ group_blk = old_fs->super->s_first_data_block;
+ for (i = 0; i < old_fs->group_desc_count;
+ i++, group_blk += fs->super->s_blocks_per_group) {
+ if (!ext2fs_bg_has_super(fs, i))
continue;
- if (diff < 0)
- diff = 0;
-
- retval = io_channel_read_blk64(fs->io, old_blk,
- fs->inode_blocks_per_group,
- rfs->itable_buf);
- if (retval)
- goto errout;
- /*
- * The end of the inode table segment often contains
- * all zeros, and we're often only moving the inode
- * table down a block or two. If so, we can optimize
- * things by not rewriting blocks that we know to be zero
- * already.
- */
- for (cp = rfs->itable_buf+size-1, n=0; n < size; n++, cp--)
- if (*cp)
- break;
- n = n >> EXT2_BLOCK_SIZE_BITS(fs->super);
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE)
- printf("%d blocks of zeros...\n", n);
-#endif
- num = fs->inode_blocks_per_group;
- if (n > diff)
- num -= n;
-
- retval = io_channel_write_blk64(fs->io, new_blk,
- num, rfs->itable_buf);
- if (retval) {
- io_channel_write_blk64(fs->io, old_blk,
- num, rfs->itable_buf);
- goto errout;
- }
- if (n > diff) {
- retval = io_channel_write_blk64(fs->io,
- old_blk + fs->inode_blocks_per_group,
- diff, (rfs->itable_buf +
- (fs->inode_blocks_per_group - diff) *
- fs->blocksize));
- if (retval)
- goto errout;
- }
-
- for (blk = ext2fs_inode_table_loc(rfs->old_fs, i), j=0;
- j < fs->inode_blocks_per_group;) {
- if (new_bmap == NULL ||
- !ext2fs_test_block_bitmap2(new_bmap, blk)) {
- ext2fs_block_alloc_stats2(fs, blk, -1);
- cluster_freed = EXT2FS_CLUSTER_RATIO(fs) -
- (blk & EXT2FS_CLUSTER_MASK(fs));
- blk += cluster_freed;
- j += cluster_freed;
- continue;
- }
- blk++;
- j++;
- }

- ext2fs_inode_table_loc_set(rfs->old_fs, i, new_blk);
- ext2fs_group_desc_csum_set(rfs->old_fs, i);
- ext2fs_mark_super_dirty(rfs->old_fs);
- ext2fs_flush(rfs->old_fs);
+ meta_bg = i / meta_bg_size;
+ if (fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_META_BG &&
+ meta_bg >= fs->super->s_first_meta_bg)
+ break;

- if (rfs->progress) {
- retval = rfs->progress(rfs, E2_RSZ_MOVE_ITABLE_PASS,
- ++moved, to_move);
- if (retval)
- goto errout;
- }
+ ext2fs_mark_block_bitmap_range2(fs->block_map,
+ group_blk + old_blocks + 1,
+ new_blocks - old_blocks);
}
- mark_table_blocks(fs, fs->block_map);
- ext2fs_flush(fs);
-#ifdef RESIZE2FS_DEBUG
- if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE)
- printf("Inode table move finished.\n");
-#endif
- retval = 0;
-
errout:
- if (new_bmap)
- ext2fs_free_block_bitmap(new_bmap);
+ if (move_blocks)
+ ext2fs_free_block_bitmap(move_blocks);
return retval;
}

@@ -2100,102 +988,6 @@ static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs)
}

/*
- * This function is used when shrinking a file system. We need to
- * utilize blocks from what will be the new last block group for the
- * backup superblock and block group descriptor blocks.
- * Unfortunately, those blocks may be used by other files or fs
- * metadata blocks. We need to mark them as being in use.
- */
-static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
- ext2fs_block_bitmap meta_bmap)
-{
- ext2_filsys fs = rfs->new_fs;
- ext2_filsys old_fs = rfs->old_fs;
- errcode_t retval;
- dgrp_t old_last_bg = rfs->old_fs->group_desc_count - 1;
- dgrp_t last_bg = fs->group_desc_count - 1;
- dgrp_t g;
- blk64_t blk, sb, old_desc;
- blk_t i, num;
- int realloc = 0;
-
- if (!(fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2))
- return 0;
-
- if (last_bg >= old_last_bg)
- return 0;
-
- if (fs->super->s_backup_bgs[0] == old_fs->super->s_backup_bgs[0] &&
- fs->super->s_backup_bgs[1] == old_fs->super->s_backup_bgs[1])
- return 0;
-
- if (fs->super->s_backup_bgs[0] != last_bg &&
- fs->super->s_backup_bgs[1] != last_bg)
- return 0;
-
- if (old_fs->super->s_backup_bgs[0] == last_bg ||
- old_fs->super->s_backup_bgs[1] == last_bg)
- return 0;
-
- retval = ext2fs_super_and_bgd_loc2(rfs->new_fs, last_bg,
- &sb, &old_desc, NULL, &num);
- if (retval)
- return retval;
-
- if (last_bg && !sb) {
- fputs(_("Should never happen! No sb in last super_sparse bg?\n"),
- stderr);
- exit(1);
- }
- if (old_desc && old_desc != sb+1) {
- fputs(_("Should never happen! Unexpected old_desc in "
- "super_sparse bg?\n"),
- stderr);
- exit(1);
- }
- num = (old_desc) ? num : 1;
-
- /* Reserve the backup blocks */
- ext2fs_mark_block_bitmap_range2(fs->block_map, sb, num);
-
- for (g = 0; g < fs->group_desc_count; g++) {
- blk64_t mb;
-
- mb = ext2fs_block_bitmap_loc(fs, g);
- if ((mb >= sb) && (mb < sb + num)) {
- ext2fs_block_bitmap_loc_set(fs, g, 0);
- realloc = 1;
- }
- mb = ext2fs_inode_bitmap_loc(fs, g);
- if ((mb >= sb) && (mb < sb + num)) {
- ext2fs_inode_bitmap_loc_set(fs, g, 0);
- realloc = 1;
- }
- mb = ext2fs_inode_table_loc(fs, g);
- if ((mb < sb + num) &&
- (sb < mb + fs->inode_blocks_per_group)) {
- ext2fs_inode_table_loc_set(fs, g, 0);
- realloc = 1;
- }
- if (realloc) {
- retval = ext2fs_allocate_group_table(fs, g, 0);
- if (retval)
- return retval;
- }
- }
-
- for (blk = sb, i = 0; i < num; blk++, i++) {
- if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
- !ext2fs_test_block_bitmap2(meta_bmap, blk)) {
- ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
- rfs->needed_blocks++;
- }
- ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
- }
- return 0;
-}
-
-/*
* Fix the resize inode
*/
static errcode_t fix_resize_inode(ext2_filsys fs)
@@ -2320,33 +1112,6 @@ static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
return 0;
}

-/*
- * Journal may have been relocated; update the backup journal blocks
- * in the superblock.
- */
-static errcode_t fix_sb_journal_backup(ext2_filsys fs)
-{
- errcode_t retval;
- struct ext2_inode inode;
-
- if (!(fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
- return 0;
-
- /* External journal? Nothing to do. */
- if (fs->super->s_journal_dev && !fs->super->s_journal_inum)
- return 0;
-
- retval = ext2fs_read_inode(fs, fs->super->s_journal_inum, &inode);
- if (retval)
- return retval;
- memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
- fs->super->s_jnl_blocks[15] = inode.i_size_high;
- fs->super->s_jnl_blocks[16] = inode.i_size;
- fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
- ext2fs_mark_super_dirty(fs);
- return 0;
-}
-
static int calc_group_overhead(ext2_filsys fs, blk64_t grp,
int old_desc_blocks)
{
diff --git a/resize/resize2fs.h b/resize/resize2fs.h
index c53b3bee8c8d..c5d9c92c58ca 100644
--- a/resize/resize2fs.h
+++ b/resize/resize2fs.h
@@ -107,19 +107,7 @@ typedef struct ext2_resize_struct *ext2_resize_t;
struct ext2_resize_struct {
ext2_filsys old_fs;
ext2_filsys new_fs;
- ext2fs_block_bitmap reserve_blocks;
- ext2fs_block_bitmap move_blocks;
- ext2_extent bmap;
- ext2_extent imap;
- blk64_t needed_blocks;
int flags;
- char *itable_buf;

2015-08-07 10:51:49

by Jan Kara

[permalink] [raw]
Subject: [PATCH 17/19] resize2fs: Remove extent mapping code

Extent mapping code is now in libext2fs. Remove a special version from
resize2fs.

Signed-off-by: Jan Kara <[email protected]>
---
resize/Makefile.in | 8 +-
resize/extent.c | 241 -----------------------------------------------------
resize/resize2fs.h | 11 ---
3 files changed, 3 insertions(+), 257 deletions(-)
delete mode 100644 resize/extent.c

diff --git a/resize/Makefile.in b/resize/Makefile.in
index ecd8619e55a5..82ae57adc4c5 100644
--- a/resize/Makefile.in
+++ b/resize/Makefile.in
@@ -16,13 +16,11 @@ PROGS= resize2fs
TEST_PROGS= test_extent
MANPAGES= resize2fs.8

-RESIZE_OBJS= extent.o resize2fs.o main.o online.o resource_track.o \
- sim_progress.o
+RESIZE_OBJS= resize2fs.o main.o online.o resource_track.o sim_progress.o

-TEST_EXTENT_OBJS= extent.o test_extent.o
+TEST_EXTENT_OBJS= test_extent.o

-SRCS= $(srcdir)/extent.c \
- $(srcdir)/resize2fs.c \
+SRCS= $(srcdir)/resize2fs.c \
$(srcdir)/main.c \
$(srcdir)/online.c \
$(srcdir)/resource_track.c \
diff --git a/resize/extent.c b/resize/extent.c
deleted file mode 100644
index ec81b944e94a..000000000000
--- a/resize/extent.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * extent.c --- ext2 extent abstraction
- *
- * This abstraction is used to provide a compact way of representing a
- * translation table, for moving multiple contiguous ranges (extents)
- * of blocks or inodes.
- *
- * Copyright (C) 1997, 1998 by Theodore Ts'o and
- * PowerQuest, Inc.
- *
- * Copyright (C) 1999, 2000 by Theosore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include "config.h"
-#include "resize2fs.h"
-
-struct ext2_extent_entry {
- __u64 old_loc, new_loc;
- __u64 size;
-};
-
-struct _ext2_extent {
- struct ext2_extent_entry *list;
- __u64 cursor;
- __u64 size;
- __u64 num;
- __u64 sorted;
-};
-
-/*
- * Create an extent table
- */
-errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent, __u64 size)
-{
- ext2_extent extent;
- errcode_t retval;
-
- retval = ext2fs_get_mem(sizeof(struct _ext2_extent), &extent);
- if (retval)
- return retval;
- memset(extent, 0, sizeof(struct _ext2_extent));
-
- extent->size = size ? size : 50;
- extent->cursor = 0;
- extent->num = 0;
- extent->sorted = 1;
-
- retval = ext2fs_get_array(sizeof(struct ext2_extent_entry),
- extent->size, &extent->list);
- if (retval) {
- ext2fs_free_mem(&extent);
- return retval;
- }
- memset(extent->list, 0,
- sizeof(struct ext2_extent_entry) * extent->size);
- *ret_extent = extent;
- return 0;
-}
-
-/*
- * Free an extent table
- */
-void ext2fs_free_extent_table(ext2_extent extent)
-{
- if (extent->list)
- ext2fs_free_mem(&extent->list);
- extent->list = 0;
- extent->size = 0;
- extent->num = 0;
- ext2fs_free_mem(&extent);
-}
-
-/*
- * Add an entry to the extent table
- */
-errcode_t ext2fs_add_extent_entry(ext2_extent extent, __u64 old_loc, __u64 new_loc)
-{
- struct ext2_extent_entry *ent;
- errcode_t retval;
- __u64 newsize;
- __u64 curr;
-
- if (extent->num >= extent->size) {
- newsize = extent->size + 100;
- retval = ext2fs_resize_mem(sizeof(struct ext2_extent_entry) *
- extent->size,
- sizeof(struct ext2_extent_entry) *
- newsize, &extent->list);
- if (retval)
- return retval;
- extent->size = newsize;
- }
- curr = extent->num;
- ent = extent->list + curr;
- if (curr) {
- /*
- * Check to see if this can be coalesced with the last
- * extent
- */
- ent--;
- if ((ent->old_loc + ent->size == old_loc) &&
- (ent->new_loc + ent->size == new_loc)) {
- ent->size++;
- return 0;
- }
- /*
- * Now see if we're going to ruin the sorting
- */
- if (ent->old_loc + ent->size > old_loc)
- extent->sorted = 0;
- ent++;
- }
- ent->old_loc = old_loc;
- ent->new_loc = new_loc;
- ent->size = 1;
- extent->num++;
- return 0;
-}
-
-/*
- * Helper function for qsort
- */
-static EXT2_QSORT_TYPE extent_cmp(const void *a, const void *b)
-{
- const struct ext2_extent_entry *db_a;
- const struct ext2_extent_entry *db_b;
-
- db_a = (const struct ext2_extent_entry *) a;
- db_b = (const struct ext2_extent_entry *) b;
-
- return (db_a->old_loc - db_b->old_loc);
-}
-
-/*
- * Given an inode map and inode number, look up the old inode number
- * and return the new inode number.
- */
-__u64 ext2fs_extent_translate(ext2_extent extent, __u64 old_loc)
-{
- __s64 low, high, mid;
- __u64 lowval, highval;
- float range;
-
- if (!extent->sorted) {
- qsort(extent->list, extent->num,
- sizeof(struct ext2_extent_entry), extent_cmp);
- extent->sorted = 1;
- }
- low = 0;
- high = extent->num-1;
- while (low <= high) {
-#if 0
- mid = (low+high)/2;
-#else
- if (low == high)
- mid = low;
- else {
- /* Interpolate for efficiency */
- lowval = extent->list[low].old_loc;
- highval = extent->list[high].old_loc;
-
- if (old_loc < lowval)
- range = 0;
- else if (old_loc > highval)
- range = 1;
- else {
- range = ((float) (old_loc - lowval)) /
- (highval - lowval);
- if (range > 0.9)
- range = 0.9;
- if (range < 0.1)
- range = 0.1;
- }
- mid = low + ((__u64) (range * (high-low)));
- }
-#endif
- if ((old_loc >= extent->list[mid].old_loc) &&
- (old_loc < extent->list[mid].old_loc + extent->list[mid].size))
- return (extent->list[mid].new_loc +
- (old_loc - extent->list[mid].old_loc));
- if (old_loc < extent->list[mid].old_loc)
- high = mid-1;
- else
- low = mid+1;
- }
- return 0;
-}
-
-/*
- * For debugging only
- */
-void ext2fs_extent_dump(ext2_extent extent, FILE *out)
-{
- __u64 i;
- struct ext2_extent_entry *ent;
-
- fputs(_("# Extent dump:\n"), out);
- fprintf(out, _("#\tNum=%llu, Size=%llu, Cursor=%llu, Sorted=%llu\n"),
- extent->num, extent->size, extent->cursor, extent->sorted);
- for (i=0, ent=extent->list; i < extent->num; i++, ent++) {
- fprintf(out, "#\t\t %llu -> %llu (%llu)\n", ent->old_loc,
- ent->new_loc, ent->size);
- }
-}
-
-/*
- * Iterate over the contents of the extent table
- */
-errcode_t ext2fs_iterate_extent(ext2_extent extent, __u64 *old_loc,
- __u64 *new_loc, __u64 *size)
-{
- struct ext2_extent_entry *ent;
-
- if (!old_loc) {
- extent->cursor = 0;
- return 0;
- }
-
- if (extent->cursor >= extent->num) {
- *old_loc = 0;
- *new_loc = 0;
- *size = 0;
- return 0;
- }
-
- ent = extent->list + extent->cursor++;
-
- *old_loc = ent->old_loc;
- *new_loc = ent->new_loc;
- *size = ent->size;
- return 0;
-}
-
-
-
-
diff --git a/resize/resize2fs.h b/resize/resize2fs.h
index 829fcd8ea8e1..c53b3bee8c8d 100644
--- a/resize/resize2fs.h
+++ b/resize/resize2fs.h
@@ -152,17 +152,6 @@ extern errcode_t adjust_fs_info(ext2_filsys fs, ext2_filsys old_fs,
extern blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags);


-/* extent.c */
-extern errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent,
- __u64 size);
-extern void ext2fs_free_extent_table(ext2_extent extent);
-extern errcode_t ext2fs_add_extent_entry(ext2_extent extent,
- __u64 old_loc, __u64 new_loc);
-extern __u64 ext2fs_extent_translate(ext2_extent extent, __u64 old_loc);
-extern void ext2fs_extent_dump(ext2_extent extent, FILE *out);
-extern errcode_t ext2fs_iterate_extent(ext2_extent extent, __u64 *old_loc,
- __u64 *new_loc, __u64 *size);

2015-08-07 10:51:49

by Jan Kara

[permalink] [raw]
Subject: [PATCH 16/19] ext2fs: Add extent dumping function to extent mapping code

For testing extent mapping code, we will need the extent dumping
function. So copy that function over from the resize2fs version of the
code.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/extent_map.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/lib/ext2fs/extent_map.c b/lib/ext2fs/extent_map.c
index 702172b24e2b..251212d118cb 100644
--- a/lib/ext2fs/extent_map.c
+++ b/lib/ext2fs/extent_map.c
@@ -231,3 +231,20 @@ errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
*size = ent->size;
return 0;
}
+
+/*
+ * For debugging only
+ */
+void ext2fs_extent_dump(ext2_map_extent extent, FILE *out)
+{
+ __u64 i;
+ struct ext2_map_extent_entry *ent;
+
+ fputs("# Extent dump:\n", out);
+ fprintf(out, "#\tNum=%llu, Size=%llu, Cursor=%llu, Sorted=%llu\n",
+ extent->num, extent->size, extent->cursor, extent->sorted);
+ for (i=0, ent=extent->list; i < extent->num; i++, ent++) {
+ fprintf(out, "#\t\t %llu -> %llu (%llu)\n", ent->old_loc,
+ ent->new_loc, ent->size);
+ }
+}
--
2.1.4


2015-08-07 10:58:29

by Alexey Lyahkov

[permalink] [raw]
Subject: Re: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

May you don’t use a magic constants in code? It hard to maintain latter.


> On Aug 7, 2015, at 1:51 PM, Jan Kara <[email protected]> wrote:
>
> From: Jan Kara <[email protected]>
>
> We ran out of reserved inodes so bump the default number of reserved
> inodes to 64 to get some breathing space. Otherwise we have to do a full
> fs scan when increasing number of reserved inodes when some feature
> needing another reserved inode is enabled. This consumes 13.5 KB on a
> filesystem which is negligible these days.
>
> Signed-off-by: Jan Kara <[email protected]>
> ---
> lib/ext2fs/initialize.c | 2 +-
> misc/mke2fs.8.in | 8 ++++----
> misc/mke2fs.conf.5.in | 7 ++++---
> 3 files changed, 9 insertions(+), 8 deletions(-)
>
> diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
> index 75fbf8ee0061..0ecf4606ce48 100644
> --- a/lib/ext2fs/initialize.c
> +++ b/lib/ext2fs/initialize.c
> @@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,
>
> set_field(s_rev_level, EXT2_GOOD_OLD_REV);
> if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
> - set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
> + set_field(s_first_ino, 64);
> set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
> if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
> int extra_isize = sizeof(struct ext2_inode_large) -
> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> index 520a07185f9f..6eef1d97bfbf 100644
> --- a/misc/mke2fs.8.in
> +++ b/misc/mke2fs.8.in
> @@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
> @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> .TP
> .BI reserved_inodes= number
> -Specify the number of inodes reserved for system files. This number must be
> -at least 10. Currently 10 is enough but future features may require additional
> -reserved inodes. Reserving more inodes after file system is created requires
> -full file system scan so it can take a long time.
> +Specify the number of inodes reserved for system files. This number must be at
> +least 10, default is 64. Currently 10 is enough but future features may require
> +additional reserved inodes. Reserving more inodes after file system is created
> +requires full file system scan so it can take a long time.
> .RE
> .TP
> .BI \-f " fragment-size"
> diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> index 06ca9e4eabc4..b35767bfedf2 100644
> --- a/misc/mke2fs.conf.5.in
> +++ b/misc/mke2fs.conf.5.in
> @@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
> .TP
> .I reserved_inodes
> This relation specifies the default number of inodes reserved for system files.
> -The number must be at least 10. Currently 10 is enough but future features may
> -require additional reserved inodes. Reserving more inodes after file system is
> -created requires full file system scan so it can take a long time.
> +The number must be at least 10, default is 64. Currently 10 is enough but
> +future features may require additional reserved inodes. Reserving more inodes
> +after file system is created requires full file system scan so it can take a
> +long time.
> .TP
> .I undo_dir
> This relation specifies the directory where the undo file should be
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2015-08-07 10:51:49

by Jan Kara

[permalink] [raw]
Subject: [PATCH 18/19] ext2fs: Move extent mapping test

Move test program for excercising extent mapping code from resize2fs
into libext2fs since the code is now generic.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/Makefile.in | 12 +++-
lib/ext2fs/tst_extent_map.c | 127 +++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/tst_extent_map_in | 64 ++++++++++++++++++++++
resize/Makefile.in | 21 +------
resize/test_extent.c | 126 ------------------------------------------
resize/test_extent.in | 64 ----------------------
6 files changed, 203 insertions(+), 211 deletions(-)
create mode 100644 lib/ext2fs/tst_extent_map.c
create mode 100644 lib/ext2fs/tst_extent_map_in
delete mode 100644 resize/test_extent.c
delete mode 100644 resize/test_extent.in

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 02ede7bbf856..98d8d3764e05 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -488,6 +488,14 @@ tst_extents: $(srcdir)/extent.c $(DEBUG_OBJS) $(DEPSTATIC_LIBSS) libext2fs.a \
$(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) \
$(STATIC_LIBCOM_ERR) $(SYSLIBS) -I $(top_srcdir)/debugfs

+tst_extent_map: $(srcdir)/extent_map.c $(DEPSTATIC_LIBSS) \
+ $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_extent_map $(srcdir)/tst_extent_map.c \
+ $(ALL_CFLAGS) $(ALL_LDFLAGS) -DDEBUG \
+ $(STATIC_LIBSS) $(STATIC_LIBEXT2FS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
tst_libext2fs: $(DEBUG_OBJS) \
$(DEPSTATIC_LIBSS) $(STATIC_LIBE2P) $(DEPLIBUUID) libext2fs.a \
$(DEPLIBBLKID) $(DEPSTATIC_LIBCOM_ERR) $(DEPLIBQUOTA)
@@ -528,7 +536,7 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
- tst_digest_encode
+ tst_digest_encode tst_extent_map
$(TESTENV) ./tst_bitops
$(TESTENV) ./tst_badblocks
$(TESTENV) ./tst_iscan
@@ -537,6 +545,8 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
$(TESTENV) ./tst_super_size
$(TESTENV) ./tst_inode_size
$(TESTENV) ./tst_csum
+ $(TESTENV) ./tst_extent_map <tst_extent_map_in >tst_extent_map_out
+ diff $(srcdir)/tst_extent_map_in tst_extent_map_out
$(TESTENV) ./tst_inline
$(TESTENV) ./tst_inline_data
$(TESTENV) ./tst_crc32c
diff --git a/lib/ext2fs/tst_extent_map.c b/lib/ext2fs/tst_extent_map.c
new file mode 100644
index 000000000000..a00dedb2932b
--- /dev/null
+++ b/lib/ext2fs/tst_extent_map.c
@@ -0,0 +1,127 @@
+/*
+ * test_extent.c --- tester for the extent abstraction
+ *
+ * Copyright (C) 1997, 1998 by Theodore Ts'o and
+ * PowerQuest, Inc.
+ *
+ * Copyright (C) 1999, 2000 by Theosore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+
+#include "move.h"
+
+void do_test(FILE *in, FILE *out);
+
+void do_test(FILE *in, FILE *out)
+{
+ char buf[128];
+ char *cp, *cmd, *arg1, *arg2;
+ __u64 num1, num2;
+ __u64 size;
+ errcode_t retval;
+ ext2_map_extent extent = 0;
+ const char *no_table = "# No extent table\n";
+
+ while (!feof(in)) {
+ if (!fgets(buf, sizeof(buf), in))
+ break;
+ /*
+ * Ignore comments
+ */
+ if (buf[0] =='#')
+ continue;
+
+ /*
+ * Echo command
+ */
+ fputs(buf, out);
+
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = '\0';
+
+ /*
+ * Parse command line; simple, at most two arguments
+ */
+ cmd = buf;
+ num1 = num2 = 0;
+ arg1 = arg2 = 0;
+ cp = strchr(buf, ' ');
+ if (cp) {
+ *cp++ = '\0';
+ arg1 = cp;
+ num1 = strtoul(arg1, 0, 0);
+
+ cp = strchr(cp, ' ');
+ }
+ if (cp) {
+ *cp++ = '\0';
+ arg2 = cp;
+ num2 = strtoul(arg2, 0, 0);
+ }
+
+ if (!strcmp(cmd, "create")) {
+ retval = ext2fs_create_extent_table(&extent, num1);
+ if (retval) {
+ handle_error:
+ fprintf(out, "# Error: %s\n",
+ error_message(retval));
+ continue;
+ }
+ continue;
+ }
+ if (!extent) {
+ fputs(no_table, out);
+ continue;
+ }
+ if (!strcmp(cmd, "free")) {
+ ext2fs_free_extent_table(extent);
+ extent = 0;
+ } else if (!strcmp(cmd, "add")) {
+ retval = ext2fs_add_extent_entry(extent, num1, num2);
+ if (retval)
+ goto handle_error;
+ } else if (!strcmp(cmd, "lookup")) {
+ num2 = ext2fs_extent_translate(extent, num1);
+ fprintf(out, "# Answer: %llu%s\n", num2,
+ num2 ? "" : " (not found)");
+ } else if (!strcmp(cmd, "dump")) {
+ ext2fs_extent_dump(extent, out);
+ } else if (!strcmp(cmd, "iter_test")) {
+ retval = ext2fs_iterate_extent(extent, 0, 0, 0);
+ if (retval)
+ goto handle_error;
+ while (1) {
+ retval = ext2fs_iterate_extent(extent,
+ &num1, &num2, &size);
+ if (retval)
+ goto handle_error;
+ if (!size)
+ break;
+ fprintf(out, "# %llu -> %llu (%llu)\n",
+ num1, num2, size);
+ }
+ } else
+ fputs("# Syntax error\n", out);
+ }
+ if (extent)
+ ext2fs_free_extent_table(extent);
+}
+
+#ifdef __GNUC__
+#define ATTR(x) __attribute__(x)
+#else
+#define ATTR(x)
+#endif
+
+int main(int argc ATTR((unused)), char **argv ATTR((unused)))
+{
+ do_test(stdin, stdout);
+ exit(0);
+}
diff --git a/lib/ext2fs/tst_extent_map_in b/lib/ext2fs/tst_extent_map_in
new file mode 100644
index 000000000000..7edcc41898a6
--- /dev/null
+++ b/lib/ext2fs/tst_extent_map_in
@@ -0,0 +1,64 @@
+create 10
+add 10 20
+add 11 21
+add 12 22
+add 14 45
+add 16 50
+add 17 51
+dump
+# Extent dump:
+# Num=3, Size=10, Cursor=0, Sorted=1
+# 10 -> 20 (3)
+# 14 -> 45 (1)
+# 16 -> 50 (2)
+add 18 52
+dump
+# Extent dump:
+# Num=3, Size=10, Cursor=0, Sorted=1
+# 10 -> 20 (3)
+# 14 -> 45 (1)
+# 16 -> 50 (3)
+lookup 10
+# Answer: 20
+lookup 11
+# Answer: 21
+lookup 12
+# Answer: 22
+lookup 13
+# Answer: 0 (not found)
+lookup 14
+# Answer: 45
+lookup 15
+# Answer: 0 (not found)
+lookup 16
+# Answer: 50
+lookup 1
+# Answer: 0 (not found)
+lookup 50
+# Answer: 0 (not found)
+add 19 100
+add 13 5
+lookup 18
+# Answer: 52
+lookup 19
+# Answer: 100
+lookup 20
+# Answer: 0 (not found)
+lookup 12
+# Answer: 22
+lookup 13
+# Answer: 5
+dump
+# Extent dump:
+# Num=5, Size=10, Cursor=0, Sorted=1
+# 10 -> 20 (3)
+# 13 -> 5 (1)
+# 14 -> 45 (1)
+# 16 -> 50 (3)
+# 19 -> 100 (1)
+iter_test
+# 10 -> 20 (3)
+# 13 -> 5 (1)
+# 14 -> 45 (1)
+# 16 -> 50 (3)
+# 19 -> 100 (1)
diff --git a/resize/Makefile.in b/resize/Makefile.in
index 82ae57adc4c5..f67188dcfd42 100644
--- a/resize/Makefile.in
+++ b/resize/Makefile.in
@@ -13,13 +13,10 @@ LDFLAG_STATIC = @LDFLAG_STATIC@
@MCONFIG@

PROGS= resize2fs
-TEST_PROGS= test_extent
MANPAGES= resize2fs.8

RESIZE_OBJS= resize2fs.o main.o online.o resource_track.o sim_progress.o

-TEST_EXTENT_OBJS= test_extent.o
-
SRCS= $(srcdir)/resize2fs.c \
$(srcdir)/main.c \
$(srcdir)/online.c \
@@ -54,10 +51,6 @@ resize2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/resize2fs.8.in
$(E) " SUBST $@"
$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/resize2fs.8.in resize2fs.8

-test_extent: $(TEST_EXTENT_OBJS)
- $(E) " LD $@"
- $(Q) $(CC) $(ALL_LDFLAGS) -o test_extent $(TEST_EXTENT_OBJS) $(LIBS)
-
installdirs:
$(E) " MKINSTALLDIRS $(root_sbindir) $(man8dir)"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(root_sbindir) \
@@ -90,21 +83,9 @@ uninstall:
$(RM) -f $(DESTDIR)$(man8dir)/$$i; \
done

-test_extent.out: test_extent $(srcdir)/test_extent.in
- $(TESTENV) ./test_extent < $(srcdir)/test_extent.in > test_extent.out
-
-check:: test_extent.out
- $(Q) if cmp -s test_extent.out $(srcdir)/test_extent.in ; then \
- echo "Test succeeded." ; \
- else \
- echo "Test failed!" ; \
- diff test_extent.out $(srcdir)/test_extent.in ; \
- exit 1 ; \
- fi
-
clean::
$(RM) -f $(PROGS) $(TEST_PROGS) $(MANPAGES) \#* *.s *.o *.a *~ core \
- resize2fs.static test_extent.out
+ resize2fs.static

mostlyclean: clean

diff --git a/resize/test_extent.c b/resize/test_extent.c
deleted file mode 100644
index 60aa08f3577e..000000000000
--- a/resize/test_extent.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * test_extent.c --- tester for the extent abstraction
- *
- * Copyright (C) 1997, 1998 by Theodore Ts'o and
- * PowerQuest, Inc.
- *
- * Copyright (C) 1999, 2000 by Theosore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include "config.h"
-#include "resize2fs.h"
-
-void do_test(FILE *in, FILE *out);
-
-void do_test(FILE *in, FILE *out)
-{
- char buf[128];
- char *cp, *cmd, *arg1, *arg2;
- __u64 num1, num2;
- __u64 size;
- errcode_t retval;
- ext2_extent extent = 0;
- const char *no_table = "# No extent table\n";
-
- while (!feof(in)) {
- if (!fgets(buf, sizeof(buf), in))
- break;
- /*
- * Ignore comments
- */
- if (buf[0] =='#')
- continue;
-
- /*
- * Echo command
- */
- fputs(buf, out);
-
- cp = strchr(buf, '\n');
- if (cp)
- *cp = '\0';
-
- /*
- * Parse command line; simple, at most two arguments
- */
- cmd = buf;
- num1 = num2 = 0;
- arg1 = arg2 = 0;
- cp = strchr(buf, ' ');
- if (cp) {
- *cp++ = '\0';
- arg1 = cp;
- num1 = strtoul(arg1, 0, 0);
-
- cp = strchr(cp, ' ');
- }
- if (cp) {
- *cp++ = '\0';
- arg2 = cp;
- num2 = strtoul(arg2, 0, 0);
- }
-
- if (!strcmp(cmd, "create")) {
- retval = ext2fs_create_extent_table(&extent, num1);
- if (retval) {
- handle_error:
- fprintf(out, "# Error: %s\n",
- error_message(retval));
- continue;
- }
- continue;
- }
- if (!extent) {
- fputs(no_table, out);
- continue;
- }
- if (!strcmp(cmd, "free")) {
- ext2fs_free_extent_table(extent);
- extent = 0;
- } else if (!strcmp(cmd, "add")) {
- retval = ext2fs_add_extent_entry(extent, num1, num2);
- if (retval)
- goto handle_error;
- } else if (!strcmp(cmd, "lookup")) {
- num2 = ext2fs_extent_translate(extent, num1);
- fprintf(out, "# Answer: %llu%s\n", num2,
- num2 ? "" : " (not found)");
- } else if (!strcmp(cmd, "dump")) {
- ext2fs_extent_dump(extent, out);
- } else if (!strcmp(cmd, "iter_test")) {
- retval = ext2fs_iterate_extent(extent, 0, 0, 0);
- if (retval)
- goto handle_error;
- while (1) {
- retval = ext2fs_iterate_extent(extent,
- &num1, &num2, &size);
- if (retval)
- goto handle_error;
- if (!size)
- break;
- fprintf(out, "# %llu -> %llu (%llu)\n",
- num1, num2, size);
- }
- } else
- fputs("# Syntax error\n", out);
- }
- if (extent)
- ext2fs_free_extent_table(extent);
-}
-
-#ifdef __GNUC__
-#define ATTR(x) __attribute__(x)
-#else
-#define ATTR(x)
-#endif
-
-int main(int argc ATTR((unused)), char **argv ATTR((unused)))
-{
- do_test(stdin, stdout);
- exit(0);
-}
diff --git a/resize/test_extent.in b/resize/test_extent.in
deleted file mode 100644
index 7edcc41898a6..000000000000
--- a/resize/test_extent.in
+++ /dev/null
@@ -1,64 +0,0 @@
-create 10
-add 10 20
-add 11 21
-add 12 22
-add 14 45
-add 16 50
-add 17 51
-dump
-# Extent dump:
-# Num=3, Size=10, Cursor=0, Sorted=1
-# 10 -> 20 (3)
-# 14 -> 45 (1)
-# 16 -> 50 (2)
-add 18 52
-dump
-# Extent dump:
-# Num=3, Size=10, Cursor=0, Sorted=1
-# 10 -> 20 (3)
-# 14 -> 45 (1)
-# 16 -> 50 (3)
-lookup 10
-# Answer: 20
-lookup 11
-# Answer: 21
-lookup 12
-# Answer: 22
-lookup 13
-# Answer: 0 (not found)
-lookup 14
-# Answer: 45
-lookup 15
-# Answer: 0 (not found)
-lookup 16
-# Answer: 50
-lookup 1
-# Answer: 0 (not found)
-lookup 50
-# Answer: 0 (not found)
-add 19 100
-add 13 5
-lookup 18
-# Answer: 52
-lookup 19
-# Answer: 100
-lookup 20
-# Answer: 0 (not found)
-lookup 12
-# Answer: 22
-lookup 13
-# Answer: 5
-dump
-# Extent dump:
-# Num=5, Size=10, Cursor=0, Sorted=1
-# 10 -> 20 (3)
-# 13 -> 5 (1)
-# 14 -> 45 (1)
-# 16 -> 50 (3)
-# 19 -> 100 (1)
-iter_test
-# 10 -> 20 (3)
-# 13 -> 5 (1)
-# 14 -> 45 (1)
-# 16 -> 50 (3)
-# 19 -> 100 (1)
--
2.1.4


2015-08-07 10:51:48

by Jan Kara

[permalink] [raw]
Subject: [PATCH 15/19] resize2fs: Remove duplicit condition

Signed-off-by: Jan Kara <[email protected]>
---
resize/resize2fs.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 9a8be30bbce1..22300836300d 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -682,19 +682,9 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
}

/*
- * If we are shrinking the number block groups, we're done and
+ * If we are not increasing the number block groups, we're done and
* can exit now.
*/
- if (rfs->old_fs->group_desc_count > fs->group_desc_count) {
- retval = 0;
- goto errout;
- }

2015-08-07 10:51:47

by Jan Kara

[permalink] [raw]
Subject: [PATCH 09/19] ext2fs: Implement inode moving in libext2fs

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/move.c | 422 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/move.h | 1 +
2 files changed, 423 insertions(+)

diff --git a/lib/ext2fs/move.c b/lib/ext2fs/move.c
index 5fc7a5fd53b6..6e286f118465 100644
--- a/lib/ext2fs/move.c
+++ b/lib/ext2fs/move.c
@@ -795,3 +795,425 @@ out:

return retval;
}
+
+static int add_dir_block(ext2_filsys fs, blk64_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct update_ref_process_block_struct *pb;
+ errcode_t retval;
+
+ pb = (struct update_ref_process_block_struct *) priv_data;
+ retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, *block_nr,
+ blockcnt);
+ if (retval) {
+ pb->error = retval;
+ return BLOCK_ABORT;
+ }
+ return 0;
+}
+static errcode_t build_dblist(ext2_filsys fs)
+{
+ errcode_t retval;
+ ext2_inode_scan scan = NULL;
+ ext2_ino_t ino;
+ struct ext2_inode *inode = NULL;
+ int inode_size;
+ char *block_buf = NULL;
+ struct ext2fs_numeric_progress_struct progress;
+ struct update_ref_process_block_struct pb;
+
+ retval = ext2fs_init_dblist(fs, NULL);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_get_array(fs->blocksize, 3, &block_buf);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ goto out;
+
+ if (fs->progress_ops) {
+ if (fs->progress_ops->init)
+ fs->progress_ops->init(fs, &progress,
+ "Building list of directory blocks",
+ fs->group_desc_count);
+ ext2fs_set_inode_callback(scan, progress_callback, &progress);
+ }
+
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = malloc(inode_size);
+ if (!inode) {
+ retval = ENOMEM;
+ goto out;
+ }
+ pb.inode = inode;
+ pb.error = 0;
+ pb.bmap = NULL;
+
+ while (1) {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size);
+ if (retval)
+ goto out_progress;
+ if (!ino)
+ break;
+
+ if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
+ continue; /* inode not in use */
+
+ if (!LINUX_S_ISDIR(inode->i_mode))
+ continue;
+
+ if (ext2fs_inode_has_valid_blocks2(fs, inode)) {
+ pb.ino = ino;
+ retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
+ add_dir_block,
+ &pb);
+ if (retval)
+ goto out_progress;
+ if (pb.error) {
+ retval = pb.error;
+ goto out_progress;
+ }
+ } else if (inode->i_flags & EXT4_INLINE_DATA_FL) {
+ /* Add inline directory inodes to the list */
+ retval = ext2fs_add_dir_block2(fs->dblist, ino, 0, 0);
+ if (retval)
+ goto out_progress;
+ }
+ }
+out_progress:
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &progress, NULL);
+out:
+ if (scan)
+ ext2fs_close_inode_scan(scan);
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ if (inode)
+ free(inode);
+ if (retval && fs->dblist) {
+ ext2fs_free_dblist(fs->dblist);
+ fs->dblist = NULL;
+ }
+ return retval;
+}
+
+/* Allocate space for inodes that need moving and move them there */
+static errcode_t alloc_copy_inodes(ext2_filsys fs, ext2fs_inode_bitmap move_map,
+ ext2_map_extent imap)
+{
+ errcode_t retval;
+ __u64 ino;
+ ext2_ino_t new_ino;
+ dgrp_t group;
+ int inode_size;
+ struct ext2_inode *inode = NULL;
+ ext2fs_inode_bitmap merged_map = NULL;
+ struct ext2fs_numeric_progress_struct progress;
+
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = malloc(inode_size);
+ if (!inode) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ retval = ext2fs_copy_bitmap(fs->inode_map, &merged_map);
+ if (retval)
+ goto out;
+
+ for (ino = 1; ino <= fs->super->s_inodes_count; ino++) {
+ if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino) &&
+ ext2fs_test_inode_bitmap2(move_map, ino))
+ ext2fs_mark_inode_bitmap2(merged_map, ino);
+ }
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ fs->progress_ops->init(fs, &progress, "Moving inodes",
+ fs->group_desc_count);
+ for (group = 0; group < fs->group_desc_count; group++) {
+ if (fs->progress_ops && fs->progress_ops->update) {
+ io_channel_flush(fs->io);
+ fs->progress_ops->update(fs, &progress, group);
+ }
+ if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))
+ continue;
+
+ for (ino = fs->super->s_inodes_per_group * group + 1;
+ ino <= fs->super->s_inodes_count &&
+ ino <= fs->super->s_inodes_per_group * (group + 1);
+ ino++) {
+ if (!ext2fs_fast_test_inode_bitmap2(move_map, ino))
+ continue;
+
+ retval = ext2fs_read_inode_full(fs, ino, inode,
+ inode_size);
+ if (retval)
+ goto out_progress;
+
+ if (inode->i_links_count == 0 &&
+ ino != EXT2_RESIZE_INO)
+ continue; /* inode not in use */
+
+ retval = ext2fs_new_inode(fs, 0, 0, merged_map,
+ &new_ino);
+ if (retval)
+ goto out_progress;
+ ext2fs_inode_alloc_stats2(fs, new_ino, +1,
+ LINUX_S_ISDIR(inode->i_mode));
+ ext2fs_inode_alloc_stats2(fs, ino, -1,
+ LINUX_S_ISDIR(inode->i_mode));
+ ext2fs_mark_inode_bitmap2(merged_map, new_ino);
+ inode->i_ctime = time(0);
+ retval = ext2fs_write_inode_full(fs, new_ino, inode,
+ inode_size);
+ if (retval)
+ goto out_progress;
+
+ retval = ext2fs_add_extent_entry(imap, ino, new_ino);
+ if (retval)
+ goto out_progress;
+ }
+ }
+ io_channel_flush(fs->io);
+out_progress:
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &progress, NULL);
+out:
+ if (inode)
+ free(inode);
+ if (merged_map)
+ ext2fs_free_inode_bitmap(merged_map);
+ return retval;
+}
+
+struct dblist_scan_data {
+ ext2_filsys fs;
+ blk_t cur_block;
+ blk_t blocks;
+ ext2_ino_t last_dir_ino;
+ int dir_moved;
+ int times_updated;
+ errcode_t error;
+ ext2_map_extent imap;
+ struct ext2fs_numeric_progress_struct progress;
+};
+
+static int remap_db_entry(ext2_filsys fs, struct ext2_db_entry2 *db_info,
+ void *priv_data)
+{
+ struct dblist_scan_data *data = priv_data;
+ __u64 new_ino;
+
+ new_ino = ext2fs_extent_translate(data->imap, db_info->ino);
+ if (new_ino)
+ db_info->ino = new_ino;
+ if (fs->progress_ops && fs->progress_ops->update)
+ fs->progress_ops->update(fs, &data->progress,
+ data->cur_block++);
+ return 0;
+}
+
+/* Update inode numbers in fs->dblist */
+static errcode_t rewrite_dblist_refs(ext2_filsys fs, ext2_map_extent imap)
+{
+ errcode_t retval;
+ struct dblist_scan_data data;
+
+ data.fs = fs;
+ data.cur_block = 0;
+ data.blocks = ext2fs_dblist_count2(fs->dblist);
+ data.last_dir_ino = 0;
+ data.error = 0;
+ data.imap = imap;
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ fs->progress_ops->init(fs, &data.progress,
+ "Remapping list of directory blocks",
+ data.blocks);
+
+ retval = ext2fs_dblist_iterate2(fs->dblist, remap_db_entry, &data);
+ if (retval)
+ return retval;
+ if (data.error)
+ return data.error;
+
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &data.progress, NULL);
+ return 0;
+}
+
+static int check_and_change_inodes(ext2_ino_t dir,
+ int entry EXT2FS_ATTR((unused)),
+ struct ext2_dir_entry *dirent, int offset,
+ int blocksize EXT2FS_ATTR((unused)),
+ char *buf EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct dblist_scan_data *data = priv_data;
+ struct ext2_inode inode;
+ ext2_ino_t new_ino;
+ errcode_t retval;
+ int ret = 0;
+
+ if (data->last_dir_ino != dir) {
+ data->last_dir_ino = dir;
+ data->times_updated = 0;
+ data->dir_moved = 0;
+ /*
+ * If we have checksums enabled and the has moved, then we must
+ * rewrite all dir blocks with new checksums.
+ */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(data->fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ ext2fs_extent_translate(data->imap, dir))
+ data->dir_moved = 1;
+ }
+
+ if (data->dir_moved)
+ ret |= DIRENT_CHANGED;
+
+ if (!dirent->inode)
+ return ret;
+
+ new_ino = ext2fs_extent_translate(data->imap, dirent->inode);
+ if (!new_ino)
+ return ret;
+ dirent->inode = new_ino;
+ ret |= DIRENT_CHANGED;
+
+ /* Update directory mtime and ctime for each dir */
+ if (!data->times_updated) {
+ retval = ext2fs_read_inode(data->fs, dir, &inode);
+ if (retval == 0) {
+ inode.i_mtime = inode.i_ctime = time(0);
+ retval = ext2fs_write_inode(data->fs, dir, &inode);
+ if (retval) {
+ data->error = retval;
+ ret |= DIRENT_ABORT;
+ }
+ }
+ data->times_updated = 1;
+ }
+
+ if (data->fs->progress_ops && data->fs->progress_ops->update &&
+ !offset) {
+ io_channel_flush(data->fs->io);
+ data->fs->progress_ops->update(data->fs, &data->progress,
+ data->cur_block++);
+ }
+ return ret;
+}
+
+/* Scan all directory blocks and update inode references */
+static errcode_t fix_inode_refs(ext2_filsys fs, ext2_map_extent imap)
+{
+ errcode_t retval;
+ struct dblist_scan_data data;
+
+ data.fs = fs;
+ data.cur_block = 0;
+ data.blocks = ext2fs_dblist_count2(fs->dblist);
+ data.last_dir_ino = 0;
+ data.error = 0;
+ data.imap = imap;
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ fs->progress_ops->init(fs, &data.progress,
+ "Updating inode references",
+ data.blocks);
+
+ /*
+ * dblist still has old inode numbers so iteration will use inodes
+ * at old positions. That is fine though because we didn't clobber
+ * that space yet.
+ */
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ retval = ext2fs_dblist_dir_iterate(fs->dblist,
+ DIRENT_FLAG_INCLUDE_EMPTY, 0,
+ check_and_change_inodes, &data);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &data.progress, NULL);
+ if (retval)
+ return retval;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
+
+/*
+ * Generic inode moving function. It moves inodes specified in move_map so that
+ * they become unused (it marks these inodes as free in the inode bitmap). It
+ * takes care of rewriting references from directory entries as well.
+ *
+ * The function uses fs->dblist for rewriting if present (the caller is
+ * responsible for it to be correct and complete in that case) and updates
+ * inode numbers there. Otherwise we build our own fs->dblist.
+ */
+errcode_t ext2fs_move_inodes(ext2_filsys fs, ext2fs_inode_bitmap move_map)
+{
+ errcode_t retval;
+ ext2_map_extent imap = NULL;
+ ext2_ino_t ino;
+ unsigned int inodes_to_move = 0, inodes_free = 0;
+
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval)
+ return retval;
+
+ for (ino = 1; ino <= fs->super->s_inodes_count; ino++) {
+ int used, move;
+
+ used = ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino);
+ move = ext2fs_fast_test_inode_bitmap2(move_map, ino);
+ if (!used && !move)
+ inodes_free++;
+ else if (used && move)
+ inodes_to_move++;
+ }
+
+ if (inodes_free < inodes_to_move) {
+ retval = ENOSPC;
+ goto out;
+ }
+
+ retval = ext2fs_create_extent_table(&imap, 0);
+ if (retval)
+ goto out;
+
+ if (!fs->dblist) {
+ retval = build_dblist(fs);
+ if (retval)
+ goto out;
+ }
+
+ retval = alloc_copy_inodes(fs, move_map, imap);
+ if (retval)
+ goto out;
+
+ /* Nothing to map? */
+ if (ext2fs_extent_table_empty(imap))
+ goto out;
+
+ retval = fix_inode_refs(fs, imap);
+ if (retval)
+ goto out;
+
+ retval = rewrite_dblist_refs(fs, imap);
+out:
+ if (retval && fs->dblist) {
+ /* dblist is likely invalid, free it */
+ ext2fs_free_dblist(fs->dblist);
+ fs->dblist = NULL;
+ }
+ if (imap)
+ ext2fs_free_extent_table(imap);
+ return retval;
+}
diff --git a/lib/ext2fs/move.h b/lib/ext2fs/move.h
index 8d66aa039ec0..9218d374c1eb 100644
--- a/lib/ext2fs/move.h
+++ b/lib/ext2fs/move.h
@@ -19,5 +19,6 @@ extern errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
/* move.c */
errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap move_map,
ext2fs_block_bitmap reuse_map);
+errcode_t ext2fs_move_inodes(ext2_filsys fs, ext2fs_inode_bitmap move_map);

#endif
--
2.1.4


2015-08-07 10:51:47

by Jan Kara

[permalink] [raw]
Subject: [PATCH 08/19] ext2fs: Implement block moving in libext2fs

From: Jan Kara <[email protected]>

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/Makefile.in | 6 +-
lib/ext2fs/extent_map.c | 233 ++++++++++++++
lib/ext2fs/move.c | 797 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/move.h | 23 ++
4 files changed, 1058 insertions(+), 1 deletion(-)
create mode 100644 lib/ext2fs/extent_map.c
create mode 100644 lib/ext2fs/move.c
create mode 100644 lib/ext2fs/move.h

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 26aaf6fb1a15..02ede7bbf856 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -125,7 +125,9 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
unlink.o \
valid_blk.o \
version.o \
- rbtree.o
+ rbtree.o \
+ extent_map.o \
+ move.o

SRCS= ext2_err.c \
$(srcdir)/alloc.c \
@@ -215,6 +217,8 @@ SRCS= ext2_err.c \
$(srcdir)/write_bb_file.c \
$(srcdir)/rbtree.c \
$(srcdir)/tst_libext2fs.c \
+ $(srcdir)/extent_map.c \
+ $(srcdir)/move.c \
$(DEBUG_SRCS)

HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
diff --git a/lib/ext2fs/extent_map.c b/lib/ext2fs/extent_map.c
new file mode 100644
index 000000000000..702172b24e2b
--- /dev/null
+++ b/lib/ext2fs/extent_map.c
@@ -0,0 +1,233 @@
+/*
+ * extent.c --- ext2 extent mapping abstraction
+ *
+ * This abstraction is used to provide a compact way of representing a
+ * translation table, for moving multiple contiguous ranges (extents)
+ * of blocks or inodes.
+ *
+ * Copyright (C) 1997, 1998 by Theodore Ts'o and
+ * PowerQuest, Inc.
+ *
+ * Copyright (C) 1999, 2000 by Theosore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "ext2_fs.h"
+#include "com_err.h"
+#include "move.h"
+
+struct ext2_map_extent_entry {
+ __u64 old_loc, new_loc;
+ __u64 size;
+};
+
+struct _ext2_map_extent {
+ struct ext2_map_extent_entry *list;
+ __u64 cursor;
+ __u64 size;
+ __u64 num;
+ __u64 sorted;
+};
+
+int ext2fs_extent_table_empty(ext2_map_extent extent)
+{
+ return !extent->num;
+}
+
+/*
+ * Create an extent table
+ */
+errcode_t ext2fs_create_extent_table(ext2_map_extent *ret_extent, __u64 size)
+{
+ ext2_map_extent extent;
+ errcode_t retval;
+
+ retval = ext2fs_get_mem(sizeof(struct _ext2_map_extent), &extent);
+ if (retval)
+ return retval;
+ memset(extent, 0, sizeof(struct _ext2_map_extent));
+
+ extent->size = size ? size : 50;
+ extent->cursor = 0;
+ extent->num = 0;
+ extent->sorted = 1;
+
+ retval = ext2fs_get_array(sizeof(struct ext2_map_extent_entry),
+ extent->size, &extent->list);
+ if (retval) {
+ ext2fs_free_mem(&extent);
+ return retval;
+ }
+ memset(extent->list, 0,
+ sizeof(struct ext2_map_extent_entry) * extent->size);
+ *ret_extent = extent;
+ return 0;
+}
+
+/*
+ * Free an extent table
+ */
+void ext2fs_free_extent_table(ext2_map_extent extent)
+{
+ if (extent->list)
+ ext2fs_free_mem(&extent->list);
+ extent->list = 0;
+ extent->size = 0;
+ extent->num = 0;
+ ext2fs_free_mem(&extent);
+}
+
+/*
+ * Add an entry to the extent table
+ */
+errcode_t ext2fs_add_extent_entry(ext2_map_extent extent, __u64 old_loc,
+ __u64 new_loc)
+{
+ struct ext2_map_extent_entry *ent;
+ errcode_t retval;
+ __u64 newsize;
+ __u64 curr;
+
+ if (extent->num >= extent->size) {
+ newsize = extent->size + 100;
+ retval = ext2fs_resize_mem(
+ sizeof(struct ext2_map_extent_entry) * extent->size,
+ sizeof(struct ext2_map_extent_entry) * newsize,
+ &extent->list);
+ if (retval)
+ return retval;
+ extent->size = newsize;
+ }
+ curr = extent->num;
+ ent = extent->list + curr;
+ if (curr) {
+ /*
+ * Check to see if this can be coalesced with the last
+ * extent
+ */
+ ent--;
+ if ((ent->old_loc + ent->size == old_loc) &&
+ (ent->new_loc + ent->size == new_loc)) {
+ ent->size++;
+ return 0;
+ }
+ /*
+ * Now see if we're going to ruin the sorting
+ */
+ if (ent->old_loc + ent->size > old_loc)
+ extent->sorted = 0;
+ ent++;
+ }
+ ent->old_loc = old_loc;
+ ent->new_loc = new_loc;
+ ent->size = 1;
+ extent->num++;
+ return 0;
+}
+
+/*
+ * Helper function for qsort
+ */
+static EXT2_QSORT_TYPE extent_cmp(const void *a, const void *b)
+{
+ const struct ext2_map_extent_entry *db_a;
+ const struct ext2_map_extent_entry *db_b;
+
+ db_a = (const struct ext2_map_extent_entry *) a;
+ db_b = (const struct ext2_map_extent_entry *) b;
+
+ return (db_a->old_loc - db_b->old_loc);
+}
+
+/*
+ * Given an inode map and inode number, look up the old inode number
+ * and return the new inode number.
+ */
+__u64 ext2fs_extent_translate(ext2_map_extent extent, __u64 old_loc)
+{
+ __s64 low, high, mid;
+ __u64 lowval, highval;
+ float range;
+
+ if (!extent->sorted) {
+ qsort(extent->list, extent->num,
+ sizeof(struct ext2_map_extent_entry), extent_cmp);
+ extent->sorted = 1;
+ }
+ low = 0;
+ high = extent->num-1;
+ while (low <= high) {
+#if 0
+ mid = (low+high)/2;
+#else
+ if (low == high)
+ mid = low;
+ else {
+ /* Interpolate for efficiency */
+ lowval = extent->list[low].old_loc;
+ highval = extent->list[high].old_loc;
+
+ if (old_loc < lowval)
+ range = 0;
+ else if (old_loc > highval)
+ range = 1;
+ else {
+ range = ((float) (old_loc - lowval)) /
+ (highval - lowval);
+ if (range > 0.9)
+ range = 0.9;
+ if (range < 0.1)
+ range = 0.1;
+ }
+ mid = low + ((__u64) (range * (high-low)));
+ }
+#endif
+ if ((old_loc >= extent->list[mid].old_loc) &&
+ (old_loc < extent->list[mid].old_loc + extent->list[mid].size))
+ return (extent->list[mid].new_loc +
+ (old_loc - extent->list[mid].old_loc));
+ if (old_loc < extent->list[mid].old_loc)
+ high = mid-1;
+ else
+ low = mid+1;
+ }
+ return 0;
+}
+
+/*
+ * Iterate over the contents of the extent table
+ */
+errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
+ __u64 *new_loc, __u64 *size)
+{
+ struct ext2_map_extent_entry *ent;
+
+ if (!old_loc) {
+ extent->cursor = 0;
+ return 0;
+ }
+
+ if (extent->cursor >= extent->num) {
+ *old_loc = 0;
+ *new_loc = 0;
+ *size = 0;
+ return 0;
+ }
+
+ ent = extent->list + extent->cursor++;
+
+ *old_loc = ent->old_loc;
+ *new_loc = ent->new_loc;
+ *size = ent->size;
+ return 0;
+}
diff --git a/lib/ext2fs/move.c b/lib/ext2fs/move.c
new file mode 100644
index 000000000000..5fc7a5fd53b6
--- /dev/null
+++ b/lib/ext2fs/move.c
@@ -0,0 +1,797 @@
+#include "ext2fs.h"
+#include "ext2fsP.h"
+#include "move.h"
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+/*
+ * Functions to move blocks around
+ */
+enum mover_alloc_state {
+ AVOID_REUSE,
+ DESPERATION,
+};
+
+struct mover_alloc_data {
+ ext2_filsys fs;
+ enum mover_alloc_state alloc_state;
+ blk64_t new_blk;
+ ext2fs_block_bitmap reuse_map;
+ ext2fs_block_bitmap move_map;
+};
+
+static blk64_t get_new_block(struct mover_alloc_data *data)
+{
+ ext2_filsys fs = data->fs;
+
+ while (1) {
+ if (data->new_blk >= ext2fs_blocks_count(fs->super)) {
+ if (data->alloc_state == DESPERATION)
+ return 0;
+ data->alloc_state = DESPERATION;
+ data->new_blk = fs->super->s_first_data_block;
+ continue;
+ }
+ if (ext2fs_test_block_bitmap2(fs->block_map, data->new_blk) ||
+ ext2fs_test_block_bitmap2(data->move_map, data->new_blk) ||
+ (data->alloc_state == AVOID_REUSE &&
+ ext2fs_test_block_bitmap2(data->reuse_map,
+ data->new_blk))) {
+ data->new_blk++;
+ continue;
+ }
+ return data->new_blk;
+ }
+}
+
+static errcode_t move_get_new_block(ext2_filsys fs, blk64_t goal,
+ blk64_t *ret)
+{
+ struct mover_alloc_data *data =
+ (struct mover_alloc_data *)fs->alloc_data;
+ blk64_t blk;
+
+ blk = get_new_block(data);
+ if (!blk)
+ return ENOSPC;
+ *ret = blk;
+ return 0;
+}
+
+static errcode_t block_mover(ext2_filsys fs, ext2fs_block_bitmap move_map,
+ ext2fs_block_bitmap reuse_map,
+ ext2_map_extent bmap)
+{
+ blk64_t blk, old_blk, new_blk;
+ errcode_t retval;
+ __u64 size;
+ int c;
+ int to_move, moved;
+ ext2_badblocks_list badblock_list = 0;
+ int bb_modified = 0;
+ char *buf;
+ int buf_blocks = fs->inode_blocks_per_group;
+ struct mover_alloc_data data;
+ struct ext2fs_numeric_progress_struct progress;
+
+ retval = ext2fs_read_bb_inode(fs, &badblock_list);
+ if (retval)
+ return retval;
+
+ new_blk = fs->super->s_first_data_block;
+ retval = ext2fs_get_array(fs->blocksize, buf_blocks, &buf);
+ if (retval)
+ return retval;
+
+ data.fs = fs;
+ data.new_blk = fs->super->s_first_data_block;
+ data.alloc_state = AVOID_REUSE;
+ data.reuse_map = reuse_map;
+ data.move_map = move_map;
+ fs->alloc_data = &data;
+ ext2fs_set_alloc_block_callback(fs, move_get_new_block, NULL);
+
+ /*
+ * The first step is to figure out where all of the blocks
+ * will go.
+ */
+ to_move = moved = 0;
+ for (blk = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ blk < ext2fs_blocks_count(fs->super);
+ blk += EXT2FS_CLUSTER_RATIO(fs)) {
+ if (!ext2fs_test_block_bitmap2(fs->block_map, blk))
+ continue;
+ if (!ext2fs_test_block_bitmap2(move_map, blk))
+ continue;
+ if (ext2fs_badblocks_list_test(badblock_list, blk)) {
+ ext2fs_badblocks_list_del(badblock_list, blk);
+ bb_modified++;
+ continue;
+ }
+
+ new_blk = get_new_block(&data);
+ if (!new_blk) {
+ retval = ENOSPC;
+ goto errout;
+ }
+ ext2fs_block_alloc_stats2(fs, new_blk, +1);
+ ext2fs_add_extent_entry(bmap, EXT2FS_B2C(fs, blk),
+ EXT2FS_B2C(fs, new_blk));
+ to_move++;
+ }
+
+ if (to_move == 0) {
+ retval = -1;
+ goto errout;
+ }
+
+ /*
+ * Step two is to actually move the blocks
+ */
+ retval = ext2fs_iterate_extent(bmap, 0, 0, 0);
+ if (retval)
+ goto errout;
+
+ if (fs->progress_ops && fs->progress_ops->init) {
+ (fs->progress_ops->init)(fs, &progress, "Relocating blocks",
+ to_move);
+ }
+ while (1) {
+ retval = ext2fs_iterate_extent(bmap, &old_blk, &new_blk, &size);
+ if (retval)
+ goto errout_progress;
+ if (!size)
+ break;
+ old_blk = EXT2FS_C2B(fs, old_blk);
+ new_blk = EXT2FS_C2B(fs, new_blk);
+ size = EXT2FS_C2B(fs, size);
+ do {
+ c = min(size, buf_blocks);
+ retval = io_channel_read_blk64(fs->io, old_blk, c, buf);
+ if (retval)
+ goto errout_progress;
+ retval = io_channel_write_blk64(fs->io, new_blk, c,
+ buf);
+ if (retval)
+ goto errout_progress;
+ size -= c;
+ new_blk += c;
+ old_blk += c;
+ moved += c;
+ if (fs->progress_ops && fs->progress_ops->update) {
+ io_channel_flush(fs->io);
+ fs->progress_ops->update(fs, &progress, moved);
+ }
+ } while (size > 0);
+ io_channel_flush(fs->io);
+ }
+errout_progress:
+ if (fs->progress_ops && fs->progress_ops->close) {
+ fs->progress_ops->close(fs, &progress, NULL);
+ }
+errout:
+ ext2fs_set_alloc_block_callback(fs, NULL, NULL);
+ fs->alloc_data = NULL;
+
+ if (badblock_list) {
+ if (!retval && bb_modified)
+ retval = ext2fs_update_bb_inode(fs, badblock_list);
+ ext2fs_badblocks_list_free(badblock_list);
+ }
+ ext2fs_free_mem(&buf);
+
+ return retval;
+}
+
+/*
+ * Functions to update block references from inode
+ */
+
+/*
+ * The extent translation table is stored in clusters so we need to
+ * take special care when mapping a source block number to its
+ * destination block number.
+ */
+static __u64 extent_translate(ext2_filsys fs, ext2_map_extent extent,
+ __u64 old_loc)
+{
+ __u64 new_block = EXT2FS_C2B(fs,
+ ext2fs_extent_translate(extent, EXT2FS_B2C(fs, old_loc)));
+
+ if (new_block != 0)
+ new_block += old_loc & (EXT2FS_CLUSTER_RATIO(fs) - 1);
+ return new_block;
+}
+
+static errcode_t migrate_ea_block(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ ext2_map_extent bmap)
+{
+ char *buf = NULL;
+ blk64_t new_block;
+ errcode_t err = 0;
+
+ /* No EA block? Quit early. */
+ if (ext2fs_file_acl_block(fs, inode) == 0)
+ return 0;
+ new_block = extent_translate(fs, bmap,
+ ext2fs_file_acl_block(fs, inode));
+ if (new_block == 0)
+ return 0;
+
+ /* Set the new ACL block */
+ ext2fs_file_acl_block_set(fs, inode, new_block);
+
+ /* Update checksum */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ err = ext2fs_get_mem(fs->blocksize, &buf);
+ if (err)
+ return err;
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ err = ext2fs_read_ext_attr3(fs, new_block, buf, ino);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (err)
+ goto out;
+ err = ext2fs_write_ext_attr3(fs, new_block, buf, ino);
+ if (err)
+ goto out;
+ }
+ err = ext2fs_write_inode_full(fs, ino, inode,
+ EXT2_INODE_SIZE(fs->super));
+out:
+ ext2fs_free_mem(&buf);
+ return err;
+}
+
+struct update_ref_process_block_struct {
+ ext2_ino_t ino;
+ struct ext2_inode * inode;
+ ext2_map_extent bmap;
+ errcode_t error;
+};
+
+static int update_ref_process_block(ext2_filsys fs, blk64_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk64_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct update_ref_process_block_struct *pb;
+ blk64_t block, new_block;
+ int ret = 0;
+
+ pb = (struct update_ref_process_block_struct *) priv_data;
+ block = *block_nr;
+ new_block = extent_translate(fs, pb->bmap, block);
+ if (new_block) {
+ *block_nr = new_block;
+ ret |= BLOCK_CHANGED;
+ } else
+ new_block = block;
+
+ /* Add dir block to dblist */
+ if (LINUX_S_ISDIR(pb->inode->i_mode)) {
+ errcode_t retval;
+
+ retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, new_block,
+ blockcnt);
+ if (retval) {
+ pb->error = retval;
+ ret |= BLOCK_ABORT;
+ }
+ }
+ return ret;
+}
+
+static errcode_t progress_callback(ext2_filsys fs,
+ ext2_inode_scan scan EXT2FS_ATTR((unused)),
+ dgrp_t group, void * priv_data)
+{
+ struct ext2fs_numeric_progress_struct *progress = priv_data;
+
+ io_channel_flush(fs->io);
+ if (fs->progress_ops->update)
+ (fs->progress_ops->update)(fs, progress, group + 1);
+
+ return 0;
+}
+
+/*
+ * Scan all inodes and update block references to new blocks. Also build new
+ * fs->dblist.
+ */
+static errcode_t fix_block_refs(ext2_filsys fs, ext2_map_extent bmap)
+{
+ struct update_ref_process_block_struct pb;
+ ext2_ino_t ino;
+ struct ext2_inode *inode = NULL;
+ ext2_inode_scan scan = NULL;
+ errcode_t retval;
+ char *block_buf = 0;
+ int inode_size;
+ struct ext2fs_numeric_progress_struct progress;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_get_array(fs->blocksize, 3, &block_buf);
+ if (retval)
+ goto errout;
+
+ /* Free old dblist, it needn't be valid after we moved blocks anyway */
+ if (fs->dblist) {
+ ext2fs_free_dblist(fs->dblist);
+ fs->dblist = NULL;
+ }
+ retval = ext2fs_init_dblist(fs, NULL);
+ if (retval)
+ goto errout;
+
+ if (fs->progress_ops) {
+ if (fs->progress_ops->init)
+ fs->progress_ops->init(fs, &progress,
+ "Updating block references",
+ fs->group_desc_count);
+ ext2fs_set_inode_callback(scan, progress_callback, &progress);
+ }
+
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = malloc(inode_size);
+ if (!inode) {
+ retval = ENOMEM;
+ goto errout_progress;
+ }
+
+ pb.inode = inode;
+ pb.error = 0;
+ pb.bmap = bmap;
+
+ while (1) {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size);
+ if (retval)
+ goto errout_progress;
+ if (!ino)
+ break;
+
+ if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
+ continue; /* inode not in use */
+
+ /* Remap EA block */
+ retval = migrate_ea_block(fs, ino, inode, bmap);
+ if (retval)
+ goto errout_progress;
+
+ /*
+ * Update inodes to point to new blocks; schedule directory
+ * blocks for inode remapping. Need to write out dir blocks
+ * with new inode numbers if we have metadata_csum enabled.
+ */
+ if (ext2fs_inode_has_valid_blocks2(fs, inode)) {
+ pb.ino = ino;
+ fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
+ update_ref_process_block,
+ &pb);
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ if (retval)
+ goto errout_progress;
+ if (pb.error) {
+ retval = pb.error;
+ goto errout_progress;
+ }
+ } else if (inode->i_flags & EXT4_INLINE_DATA_FL &&
+ LINUX_S_ISDIR(inode->i_mode)) {
+ /* Add inline directory inodes to the list */
+ retval = ext2fs_add_dir_block2(fs->dblist, ino, 0, 0);
+ if (retval)
+ goto errout_progress;
+ }
+ }
+ io_channel_flush(fs->io);
+errout_progress:
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &progress, NULL);
+errout:
+ if (scan)
+ ext2fs_close_inode_scan(scan);
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ free(inode);
+
+ return retval;
+}
+
+/*
+ * Move block group metadata (bitmaps, inode table)
+ */
+static errcode_t move_inode_table(ext2_filsys fs, dgrp_t group,
+ blk64_t orig_loc, char *buf)
+{
+ blk64_t new_loc, n;
+ errcode_t retval;
+ long *c;
+ size_t size;
+ unsigned long long diff;
+
+ new_loc = ext2fs_inode_table_loc(fs, group);
+ retval = io_channel_read_blk64(fs->io, orig_loc,
+ fs->inode_blocks_per_group, buf);
+ if (retval)
+ return retval;
+
+ diff = llabs(new_loc - orig_loc);
+ if (diff >= fs->inode_blocks_per_group) {
+ n = fs->inode_blocks_per_group;
+ goto skip_zero;
+ }
+ /*
+ * The end of the inode table segment often contains
+ * all zeros, and we're often only moving the inode
+ * table down a block or two. If so, we can optimize
+ * things by not rewriting blocks that we know to be zero
+ * already.
+ */
+ size = fs->inode_blocks_per_group * fs->blocksize;
+ for (c = (long *)(buf + size - sizeof(long)), n = 0; n < size;
+ n += sizeof(long), c--)
+ if (*c)
+ break;
+ n = fs->inode_blocks_per_group - (n >> EXT2_BLOCK_SIZE_BITS(fs->super));
+ /* If we don't save anything with skipping zeros, don't do it... */
+ if (n + diff >= fs->inode_blocks_per_group)
+ n = fs->inode_blocks_per_group;
+skip_zero:
+ retval = io_channel_write_blk64(fs->io, new_loc, n, buf);
+ if (retval) {
+ io_channel_write_blk64(fs->io, orig_loc, n, buf);
+ return retval;
+ }
+ if (n == fs->inode_blocks_per_group)
+ return 0;
+ /*
+ * Write zeros to overwrite non-zero values in original table. We
+ * distinguish two cases:
+ * new_loc < orig_loc orig_loc + n orig_loc + itb
+ * | |XXXXXXXXXX|00000000000000|
+ *
+ * orig_loc new_loc orig_loc + n orig_loc + itb
+ * |XXXXXXXXX|XXXXXXXXXX|00000000000000|
+ */
+ if (new_loc < orig_loc) {
+ ext2fs_zero_blocks2(fs, new_loc + n, diff, NULL, NULL);
+ } else {
+ ext2fs_zero_blocks2(fs,
+ new_loc + fs->inode_blocks_per_group - diff,
+ diff, NULL, NULL);
+ }
+}
+
+/*
+ * This helper function creates a block bitmap with all of the
+ * filesystem meta-data blocks.
+ */
+static errcode_t mark_table_blocks(ext2_filsys fs,
+ ext2fs_block_bitmap bmap)
+{
+ dgrp_t i;
+ blk64_t blk;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ ext2fs_reserve_super_and_bgd(fs, i, bmap);
+
+ /*
+ * Mark the blocks used for the inode table
+ */
+ blk = ext2fs_inode_table_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap_range2(bmap, blk,
+ fs->inode_blocks_per_group);
+
+ /*
+ * Mark block used for the block bitmap
+ */
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);
+
+ /*
+ * Mark block used for the inode bitmap
+ */
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);
+ }
+ return 0;
+}
+
+/*
+ * Moves group metadata whose blocks are marked in move_map. We also clear bits
+ * in move_map corresponding to moved metadata so that subsequent block
+ * relocation functions don't try to move them.
+ */
+static errcode_t ext2fs_move_group_metadata(ext2_filsys fs,
+ ext2fs_block_bitmap move_map)
+{
+ dgrp_t i;
+ blk64_t b, table_loc;
+ int relocate, move_itable;
+ char *buf;
+ errcode_t retval;
+ ext2fs_block_bitmap cluster_bmap = NULL, new_cluster_bmap = NULL;
+ struct ext2fs_numeric_progress_struct progress;
+ ext2fs_block_bitmap merged_map = NULL;
+ int cluster = 0;
+
+ retval = ext2fs_get_array(fs->blocksize, fs->inode_blocks_per_group,
+ &buf);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_copy_bitmap(fs->block_map, &merged_map);
+ if (retval)
+ goto out;
+
+ for (b = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ b < ext2fs_blocks_count(fs->super);
+ b += EXT2FS_CLUSTER_RATIO(fs)) {
+ if (!ext2fs_test_block_bitmap2(fs->block_map, b) &&
+ ext2fs_test_block_bitmap2(move_map, b))
+ ext2fs_mark_block_bitmap2(merged_map, b);
+ }
+
+ if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ /*
+ * For clustered allocation we cannot immediately tell which
+ * clusters remain used and which become free since clusters
+ * can be shared by different group metadata. We thus update
+ * bitmaps only after all the metadata is relocated.
+ */
+ cluster = 1;
+
+ retval = ext2fs_allocate_block_bitmap(fs, "cluster meta blocks",
+ &cluster_bmap);
+ if (retval)
+ goto out;
+
+ retval = mark_table_blocks(fs, cluster_bmap);
+ if (retval)
+ goto out;
+ }
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ fs->progress_ops->init(fs, &progress, "Moving group metadata",
+ fs->group_desc_count);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ relocate = 0;
+ move_itable = 0;
+ b = ext2fs_block_bitmap_loc(fs, i);
+ if (ext2fs_test_block_bitmap2(move_map, b)) {
+ ext2fs_block_bitmap_loc_set(fs, i, 0);
+ if (!cluster)
+ ext2fs_block_alloc_stats2(fs, b, -1);
+ /*
+ * Mark bitmap dirty so that it gets written out to the
+ * new location
+ */
+ ext2fs_mark_bb_dirty(fs);
+ relocate = 1;
+ }
+
+ b = ext2fs_inode_bitmap_loc(fs, i);
+ if (ext2fs_test_block_bitmap2(move_map, b)) {
+ ext2fs_inode_bitmap_loc_set(fs, i, 0);
+ if (!cluster)
+ ext2fs_block_alloc_stats2(fs, b, -1);
+ /*
+ * Mark bitmap dirty so that it gets written out to the
+ * new location
+ */
+ ext2fs_mark_ib_dirty(fs);
+ relocate = 1;
+ }
+
+ table_loc = ext2fs_inode_table_loc(fs, i);
+ for (b = 0; b < fs->inode_blocks_per_group; b++) {
+ if (ext2fs_test_block_bitmap2(move_map,
+ table_loc + b)) {
+ ext2fs_inode_table_loc_set(fs, i, 0);
+ relocate = 1;
+ move_itable = 1;
+ break;
+ }
+ }
+
+ if (relocate) {
+ if (move_itable && !cluster) {
+ /*
+ * Mark blocks under old table as free except
+ * blocks that need moving - those must not be
+ * reallocated for any other purpose
+ */
+ for (b = 0; b < fs->inode_blocks_per_group;) {
+ ext2fs_block_alloc_stats2(fs,
+ table_loc + b,
+ -1);
+ if (!ext2fs_test_block_bitmap2(
+ move_map,
+ table_loc + b)) {
+ ext2fs_unmark_block_bitmap2(
+ merged_map,
+ table_loc + b);
+ }
+ }
+ }
+
+ /*
+ * We allocate new blocks so that we don't reuse blocks
+ * in move_map. It would be cleaner if
+ * ext2fs_allocate_group_table2() used our block
+ * allocation function since maintaining a merged bitmap
+ * is a hassle but it does not...
+ */
+ retval = ext2fs_allocate_group_table2(fs, i, merged_map,
+ EXT2FS_ALLOC_TABLE_UPDATE_STATS |
+ EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP);
+ if (retval)
+ goto out;
+ if (move_itable) {
+ retval = move_inode_table(fs, i, table_loc,
+ buf);
+ if (retval) {
+ /*
+ * Back out setting of inode table
+ * location to avoid loosing inodes
+ */
+ ext2fs_inode_table_loc_set(fs, i,
+ table_loc);
+ goto out;
+ }
+ }
+ ext2fs_group_desc_csum_set(fs, i);
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_flush(fs);
+ }
+ if (fs->progress_ops && fs->progress_ops->update)
+ fs->progress_ops->update(fs, &progress, i);
+ }
+
+ /*
+ * For clustered allocation we now have to walk bitmaps and update them
+ * properly
+ */
+ if (cluster) {
+ retval = ext2fs_allocate_block_bitmap(fs,
+ "new cluster meta blocks",
+ &new_cluster_bmap);
+ if (retval)
+ goto out;
+
+ retval = mark_table_blocks(fs, new_cluster_bmap);
+ if (retval)
+ goto out;
+
+ for (b = fs->super->s_first_data_block;
+ b < ext2fs_blocks_count(fs->super);
+ b += EXT2FS_CLUSTER_RATIO(fs))
+ if (ext2fs_test_block_bitmap2(cluster_bmap, b) &&
+ !ext2fs_test_block_bitmap2(new_cluster_bmap, b))
+ ext2fs_block_alloc_stats2(fs, b, -1);
+ }
+ retval = 0;
+out_progress:
+ if (fs->progress_ops && fs->progress_ops->close)
+ fs->progress_ops->close(fs, &progress, NULL);
+out:
+ if (merged_map)
+ ext2fs_free_block_bitmap(merged_map);
+ if (new_cluster_bmap)
+ ext2fs_free_block_bitmap(new_cluster_bmap);
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+/*
+ * Journal may have been relocated; update the backup journal blocks
+ * in the superblock.
+ */
+static errcode_t fix_sb_journal_backup(ext2_filsys fs)
+{
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ if (!(fs->super->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return 0;
+
+ /* External journal? Nothing to do. */
+ if (fs->super->s_journal_dev && !fs->super->s_journal_inum)
+ return 0;
+
+ retval = ext2fs_read_inode(fs, fs->super->s_journal_inum, &inode);
+ if (retval)
+ return retval;
+ memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
+ fs->super->s_jnl_blocks[15] = inode.i_size_high;
+ fs->super->s_jnl_blocks[16] = inode.i_size;
+ fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+ ext2fs_mark_super_dirty(fs);
+ return 0;
+}
+
+/*
+ * Generic block moving function. It moves blocks specified in move_map so that
+ * they become unused (it marks these blocks as free in the block bitmap). It
+ * takes care of moving block bitmaps, inode bitmaps, inode tables, xattr
+ * blocks, indirect blocks, data blocks, ... It also takes care of updating
+ * backup of journal blocks in the superblock. Blocks specified in reuse_map
+ * will be used only if there are no other blocks free.
+ */
+errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap move_map,
+ ext2fs_block_bitmap reuse_map)
+{
+ errcode_t retval;
+ ext2_map_extent bmap = NULL;
+ blk64_t blk;
+ blk64_t blks_to_move = 0, blks_free = 0;
+
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval)
+ goto out;
+
+ /* Cannot proceed without our allocation callback */
+ if (fs->get_alloc_block) {
+ retval = EINVAL;
+ goto out;
+ }
+
+ for (blk = fs->super->s_first_data_block;
+ blk < ext2fs_blocks_count(fs->super);
+ blk++) {
+ int used, move;
+
+ used = ext2fs_fast_test_block_bitmap2(fs->block_map, blk);
+ move = ext2fs_fast_test_block_bitmap2(move_map, blk);
+
+ if (!used && !move)
+ blks_free++;
+ else if (used && move)
+ blks_to_move++;
+ }
+
+ if (blks_free < blks_to_move) {
+ retval = ENOSPC;
+ goto out;
+ }
+
+ retval = ext2fs_move_group_metadata(fs, move_map);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_create_extent_table(&bmap, 0);
+ if (retval)
+ goto out;
+
+ retval = block_mover(fs, move_map, reuse_map, bmap);
+ if (retval) {
+ /* block_mover() returns -1 if there's nothing to move */
+ if (retval != -1)
+ goto out;
+ retval = 0;
+ }
+
+ retval = fix_block_refs(fs, bmap);
+ if (retval)
+ goto out;
+
+ retval = fix_sb_journal_backup(fs);
+ if (retval)
+ goto out;
+
+out:
+ if (bmap)
+ ext2fs_free_extent_table(bmap);
+
+ return retval;
+}
diff --git a/lib/ext2fs/move.h b/lib/ext2fs/move.h
new file mode 100644
index 000000000000..8d66aa039ec0
--- /dev/null
+++ b/lib/ext2fs/move.h
@@ -0,0 +1,23 @@
+#ifndef _EXT2FS_MOVE_H
+#define _EXT2FS_MOVE_H
+
+#include "ext2fs.h"
+
+typedef struct _ext2_map_extent *ext2_map_extent;
+
+/* extent_map.c */
+extern int ext2fs_extent_table_empty(ext2_map_extent extent);
+extern errcode_t ext2fs_create_extent_table(ext2_map_extent *ret_extent,
+ __u64 size);
+extern void ext2fs_free_extent_table(ext2_map_extent extent);
+extern errcode_t ext2fs_add_extent_entry(ext2_map_extent extent,
+ __u64 old_loc, __u64 new_loc);
+extern __u64 ext2fs_extent_translate(ext2_map_extent extent, __u64 old_loc);
+extern errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
+ __u64 *new_loc, __u64 *size);
+
+/* move.c */
+errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap move_map,
+ ext2fs_block_bitmap reuse_map);
+
+#endif
--
2.1.4


2015-08-07 10:51:47

by Jan Kara

[permalink] [raw]
Subject: [PATCH 14/19] resize2fs: Rip out 64-bit feature handling from resize2fs

Now that tune2fs is able to properly set / clear 64BIT feature, just rip
out the handling from resize2fs as it doesn't make much sense there from
user point of view. So far this functionality wasn't in any officially
released e2fsprogs so just printing message that tune2fs should be used
should be fine.

Signed-off-by: Jan Kara <[email protected]>
---
resize/main.c | 61 ++--------
resize/resize2fs.8.in | 17 +--
resize/resize2fs.c | 331 +-------------------------------------------------
3 files changed, 12 insertions(+), 397 deletions(-)

diff --git a/resize/main.c b/resize/main.c
index 9da3a958cc01..13f62e452044 100644
--- a/resize/main.c
+++ b/resize/main.c
@@ -43,7 +43,7 @@ static char *device_name, *io_options;
static void usage (char *prog)
{
fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] "
- "[-p] device [-b|-s|new_size] [-z undo_file]\n\n"),
+ "[-p] device [new_size] [-z undo_file]\n\n"),
prog);

exit (1);
@@ -306,15 +306,13 @@ int main (int argc, char ** argv)
case 'S':
use_stride = atoi(optarg);
break;
- case 'b':
- flags |= RESIZE_ENABLE_64BIT;
- break;
- case 's':
- flags |= RESIZE_DISABLE_64BIT;
- break;
case 'z':
undo_file = optarg;
break;
+ case 'b':
+ case 's':
+ fprintf(stderr, "Please use 'tune2fs -O [^]64BIT' to "
+ "set / clear 64-bit feature.\n");
default:
usage(program_name);
}
@@ -503,10 +501,6 @@ int main (int argc, char ** argv)
if (sys_page_size > blocksize)
new_size &= ~((sys_page_size / blocksize)-1);
}
- /* If changing 64bit, don't change the filesystem size. */
- if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) {
- new_size = ext2fs_blocks_count(fs->super);
- }
if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
EXT4_FEATURE_INCOMPAT_64BIT)) {
/* Take 16T down to 2^32-1 blocks */
@@ -558,58 +552,19 @@ int main (int argc, char ** argv)
blocksize / 1024, new_size);
exit(1);
}
- if ((flags & RESIZE_DISABLE_64BIT) && (flags & RESIZE_ENABLE_64BIT)) {
- fprintf(stderr, _("Cannot set and unset 64bit feature.\n"));
- exit(1);
- } else if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) {
- if (new_size >= (1ULL << 32)) {
- fprintf(stderr, _("Cannot change the 64bit feature "
- "on a filesystem that is larger than "
- "2^32 blocks.\n"));
- exit(1);
- }
- if (mount_flags & EXT2_MF_MOUNTED) {
- fprintf(stderr, _("Cannot change the 64bit feature "
- "while the filesystem is mounted.\n"));
- exit(1);
- }
- if (flags & RESIZE_ENABLE_64BIT &&
- !EXT2_HAS_INCOMPAT_FEATURE(fs->super,
- EXT3_FEATURE_INCOMPAT_EXTENTS)) {
- fprintf(stderr, _("Please enable the extents feature "
- "with tune2fs before enabling the 64bit "
- "feature.\n"));
- exit(1);
- }
- } else if (new_size == ext2fs_blocks_count(fs->super)) {
+ if (new_size == ext2fs_blocks_count(fs->super)) {
fprintf(stderr, _("The filesystem is already %llu (%dk) "
"blocks long. Nothing to do!\n\n"), new_size,
blocksize / 1024);
exit(0);
}
- if ((flags & RESIZE_ENABLE_64BIT) &&
- EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) {
- fprintf(stderr, _("The filesystem is already 64-bit.\n"));
- exit(0);
- }
- if ((flags & RESIZE_DISABLE_64BIT) &&
- !EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) {
- fprintf(stderr, _("The filesystem is already 32-bit.\n"));
- exit(0);
- }
if (mount_flags & EXT2_MF_MOUNTED) {
bigalloc_check(fs, force);
retval = online_resize_fs(fs, mtpt, &new_size, flags);
} else {
bigalloc_check(fs, force);
- if (flags & RESIZE_ENABLE_64BIT)
- printf(_("Converting the filesystem to 64-bit.\n"));
- else if (flags & RESIZE_DISABLE_64BIT)
- printf(_("Converting the filesystem to 32-bit.\n"));
- else
- printf(_("Resizing the filesystem on "
- "%s to %llu (%dk) blocks.\n"),
- device_name, new_size, blocksize / 1024);
+ printf(_("Resizing the filesystem on %s to %llu (%dk) blocks.\n"),
+ device_name, new_size, blocksize / 1024);
retval = resize_fs(fs, &new_size, flags,
((flags & RESIZE_PERCENT_COMPLETE) ?
resize_progress_func : 0));
diff --git a/resize/resize2fs.8.in b/resize/resize2fs.8.in
index d2738e9cdf42..d9176ec7c874 100644
--- a/resize/resize2fs.8.in
+++ b/resize/resize2fs.8.in
@@ -8,7 +8,7 @@ resize2fs \- ext2/ext3/ext4 file system resizer
.SH SYNOPSIS
.B resize2fs
[
-.B \-fFpPMbs
+.B \-fFpPM
]
[
.B \-d
@@ -90,20 +90,8 @@ to shrink the size of filesystem. Then you may use
to shrink the size of the partition. When shrinking the size of
the partition, make sure you do not make it smaller than the new size
of the ext2 filesystem!
-.PP
-The
-.B \-b
-and
-.B \-s
-options enable and disable the 64bit feature, respectively. The resize2fs
-program will, of course, take care of resizing the block group descriptors
-and moving other data blocks out of the way, as needed. It is not possible
-to resize the filesystem concurrent with changing the 64bit status.
.SH OPTIONS
.TP
-.B \-b
-Turns on the 64bit feature, resizes the group descriptors as necessary, and
-moves other metadata out of the way.
.TP
.B \-d \fIdebug-flags
Turns on various resize2fs debugging features, if they have been compiled
@@ -144,9 +132,6 @@ of what the program is doing.
.B \-P
Print the minimum size of the filesystem and exit.
.TP
-.B \-s
-Turns off the 64bit feature and frees blocks that are no longer in use.
-.TP
.B \-S \fIRAID-stride
The
.B resize2fs
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index a8ba8adcfb86..9a8be30bbce1 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -55,9 +55,6 @@ static errcode_t mark_table_blocks(ext2_filsys fs,
static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs);
static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
ext2fs_block_bitmap meta_bmap);
-static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size);
-static errcode_t move_bg_metadata(ext2_resize_t rfs);
-static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs);

/*
* Some helper CPP macros
@@ -124,24 +121,6 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
if (retval)
goto errout;

- init_resource_track(&rtrack, "resize_group_descriptors", fs->io);
- retval = resize_group_descriptors(rfs, *new_size);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
- init_resource_track(&rtrack, "move_bg_metadata", fs->io);
- retval = move_bg_metadata(rfs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
- init_resource_track(&rtrack, "zero_high_bits_in_metadata", fs->io);
- retval = zero_high_bits_in_inodes(rfs);
- if (retval)
- goto errout;
- print_resource_track(rfs, &rtrack, fs->io);
-
init_resource_track(&rtrack, "adjust_superblock", fs->io);
retval = adjust_superblock(rfs, *new_size);
if (retval)
@@ -272,304 +251,6 @@ static void adjust_reserved_gdt_blocks(ext2_filsys old_fs, ext2_filsys fs)
}
}

-/* Toggle 64bit mode */
-static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size)
-{
- void *o, *n, *new_group_desc;
- dgrp_t i;
- int copy_size;
- errcode_t retval;
-
- if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
- return 0;
-
- if (new_size != ext2fs_blocks_count(rfs->new_fs->super) ||
- ext2fs_blocks_count(rfs->new_fs->super) >= (1ULL << 32) ||
- (rfs->flags & RESIZE_DISABLE_64BIT &&
- rfs->flags & RESIZE_ENABLE_64BIT))
- return EXT2_ET_INVALID_ARGUMENT;
-
- if (rfs->flags & RESIZE_DISABLE_64BIT) {
- rfs->new_fs->super->s_feature_incompat &=
- ~EXT4_FEATURE_INCOMPAT_64BIT;
- rfs->new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
- } else if (rfs->flags & RESIZE_ENABLE_64BIT) {
- rfs->new_fs->super->s_feature_incompat |=
- EXT4_FEATURE_INCOMPAT_64BIT;
- rfs->new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
- }
-
- if (EXT2_DESC_SIZE(rfs->old_fs->super) ==
- EXT2_DESC_SIZE(rfs->new_fs->super))
- return 0;
-
- o = rfs->new_fs->group_desc;
- rfs->new_fs->desc_blocks = ext2fs_div_ceil(
- rfs->old_fs->group_desc_count,
- EXT2_DESC_PER_BLOCK(rfs->new_fs->super));
- retval = ext2fs_get_arrayzero(rfs->new_fs->desc_blocks,
- rfs->old_fs->blocksize, &new_group_desc);
- if (retval)
- return retval;
-
- n = new_group_desc;
-
- if (EXT2_DESC_SIZE(rfs->old_fs->super) <=
- EXT2_DESC_SIZE(rfs->new_fs->super))
- copy_size = EXT2_DESC_SIZE(rfs->old_fs->super);
- else
- copy_size = EXT2_DESC_SIZE(rfs->new_fs->super);
- for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
- memcpy(n, o, copy_size);
- n += EXT2_DESC_SIZE(rfs->new_fs->super);
- o += EXT2_DESC_SIZE(rfs->old_fs->super);
- }
-
- ext2fs_free_mem(&rfs->new_fs->group_desc);
- rfs->new_fs->group_desc = new_group_desc;
-
- for (i = 0; i < rfs->old_fs->group_desc_count; i++)
- ext2fs_group_desc_csum_set(rfs->new_fs, i);
-
- adjust_reserved_gdt_blocks(rfs->old_fs, rfs->new_fs);
-
- return 0;
-}
-
-/* Move bitmaps/inode tables out of the way. */
-static errcode_t move_bg_metadata(ext2_resize_t rfs)
-{
- dgrp_t i;
- blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
- ext2fs_block_bitmap old_map, new_map;
- int old, new;
- errcode_t retval;
- int cluster_ratio;
-
- if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
- return 0;
-
- retval = ext2fs_allocate_block_bitmap(rfs->old_fs, "oldfs", &old_map);
- if (retval)
- return retval;
-
- retval = ext2fs_allocate_block_bitmap(rfs->new_fs, "newfs", &new_map);
- if (retval)
- goto out;
-
- if (EXT2_HAS_INCOMPAT_FEATURE(rfs->old_fs->super,
- EXT2_FEATURE_INCOMPAT_META_BG)) {
- old_desc_blocks = rfs->old_fs->super->s_first_meta_bg;
- new_desc_blocks = rfs->new_fs->super->s_first_meta_bg;
- } else {
- old_desc_blocks = rfs->old_fs->desc_blocks +
- rfs->old_fs->super->s_reserved_gdt_blocks;
- new_desc_blocks = rfs->new_fs->desc_blocks +
- rfs->new_fs->super->s_reserved_gdt_blocks;
- }
-
- /* Construct bitmaps of super/descriptor blocks in old and new fs */
- for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
- retval = ext2fs_super_and_bgd_loc2(rfs->old_fs, i, &b, &c, &d,
- NULL);
- if (retval)
- goto out;
- if (b)
- ext2fs_mark_block_bitmap2(old_map, b);
- for (j = 0; c != 0 && j < old_desc_blocks; j++)
- ext2fs_mark_block_bitmap2(old_map, c + j);
- if (d)
- ext2fs_mark_block_bitmap2(old_map, d);
-
- retval = ext2fs_super_and_bgd_loc2(rfs->new_fs, i, &b, &c, &d,
- NULL);
- if (retval)
- goto out;
- if (b)
- ext2fs_mark_block_bitmap2(new_map, b);
- for (j = 0; c != 0 && j < new_desc_blocks; j++)
- ext2fs_mark_block_bitmap2(new_map, c + j);
- if (d)
- ext2fs_mark_block_bitmap2(new_map, d);
- }
-
- cluster_ratio = EXT2FS_CLUSTER_RATIO(rfs->new_fs);
-
- /* Find changes in block allocations for bg metadata */
- for (b = EXT2FS_B2C(rfs->old_fs,
- rfs->old_fs->super->s_first_data_block);
- b < ext2fs_blocks_count(rfs->new_fs->super);
- b += cluster_ratio) {
- old = ext2fs_test_block_bitmap2(old_map, b);
- new = ext2fs_test_block_bitmap2(new_map, b);
-
- if (old && !new) {
- /* mark old_map, unmark new_map */
- if (cluster_ratio == 1)
- ext2fs_unmark_block_bitmap2(
- rfs->new_fs->block_map, b);
- } else if (!old && new)
- ; /* unmark old_map, mark new_map */
- else {
- ext2fs_unmark_block_bitmap2(old_map, b);
- ext2fs_unmark_block_bitmap2(new_map, b);
- }
- }
-
- /*
- * new_map now shows blocks that have been newly allocated.
- * old_map now shows blocks that have been newly freed.
- */
-
- /*
- * Move any conflicting bitmaps and inode tables. Ensure that we
- * don't try to free clusters associated with bitmaps or tables.
- */
- for (i = 0; i < rfs->old_fs->group_desc_count; i++) {
- b = ext2fs_block_bitmap_loc(rfs->new_fs, i);
- if (ext2fs_test_block_bitmap2(new_map, b))
- ext2fs_block_bitmap_loc_set(rfs->new_fs, i, 0);
- else if (ext2fs_test_block_bitmap2(old_map, b))
- ext2fs_unmark_block_bitmap2(old_map, b);
-
- b = ext2fs_inode_bitmap_loc(rfs->new_fs, i);
- if (ext2fs_test_block_bitmap2(new_map, b))
- ext2fs_inode_bitmap_loc_set(rfs->new_fs, i, 0);
- else if (ext2fs_test_block_bitmap2(old_map, b))
- ext2fs_unmark_block_bitmap2(old_map, b);
-
- c = ext2fs_inode_table_loc(rfs->new_fs, i);
- for (b = 0;
- b < rfs->new_fs->inode_blocks_per_group;
- b++) {
- if (ext2fs_test_block_bitmap2(new_map, b + c))
- ext2fs_inode_table_loc_set(rfs->new_fs, i, 0);
- else if (ext2fs_test_block_bitmap2(old_map, b + c))
- ext2fs_unmark_block_bitmap2(old_map, b + c);
- }
- }
-
- /* Free unused clusters */
- for (b = 0;
- cluster_ratio > 1 && b < ext2fs_blocks_count(rfs->new_fs->super);
- b += cluster_ratio)
- if (ext2fs_test_block_bitmap2(old_map, b))
- ext2fs_unmark_block_bitmap2(rfs->new_fs->block_map, b);
-out:
- if (old_map)
- ext2fs_free_block_bitmap(old_map);
- if (new_map)
- ext2fs_free_block_bitmap(new_map);
- return retval;
-}
-
-/* Zero out the high bits of extent fields */
-static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode)
-{
- ext2_extent_handle_t handle;
- struct ext2fs_extent extent;
- int op = EXT2_EXTENT_ROOT;
- errcode_t errcode;
-
- if (!(inode->i_flags & EXT4_EXTENTS_FL))
- return 0;
-
- errcode = ext2fs_extent_open(fs, ino, &handle);
- if (errcode)
- return errcode;
-
- while (1) {
- errcode = ext2fs_extent_get(handle, op, &extent);
- if (errcode)
- break;
-
- op = EXT2_EXTENT_NEXT_SIB;
-
- if (extent.e_pblk > (1ULL << 32)) {
- extent.e_pblk &= (1ULL << 32) - 1;
- errcode = ext2fs_extent_replace(handle, 0, &extent);
- if (errcode)
- break;
- }
- }
-
- /* Ok if we run off the end */
- if (errcode == EXT2_ET_EXTENT_NO_NEXT)
- errcode = 0;
- ext2fs_extent_free(handle);
- return errcode;
-}
-
-/* Zero out the high bits of inodes. */
-static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs)
-{
- ext2_filsys fs = rfs->old_fs;
- int length = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode *inode = NULL;
- ext2_inode_scan scan = NULL;
- errcode_t retval;
- ext2_ino_t ino;
-
- if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
- return 0;
-
- if (fs->super->s_creator_os != EXT2_OS_LINUX)
- return 0;
-
- retval = ext2fs_open_inode_scan(fs, 0, &scan);
- if (retval)
- return retval;
-
- 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))
- continue;
-
- /*
- * Here's how we deal with high block number fields:
- *
- * - i_size_high has been been written out with i_size_lo
- * since the ext2 days, so no conversion is needed.
- *
- * - i_blocks_hi is guarded by both the huge_file feature and
- * inode flags and has always been written out with
- * i_blocks_lo if the feature is set. The field is only
- * ever read if both feature and inode flag are set, so
- * we don't need to zero it now.
- *
- * - i_file_acl_high can be uninitialized, so zero it if
- * it isn't already.
- */
- if (inode->osd2.linux2.l_i_file_acl_high) {
- inode->osd2.linux2.l_i_file_acl_high = 0;
- retval = ext2fs_write_inode_full(fs, ino, inode,
- length);
- if (retval)
- goto out;
- }
-
- retval = zero_high_bits_in_extents(fs, ino, inode);
- if (retval)
- goto out;
- } while (ino);
-
-out:
- if (inode)
- ext2fs_free_mem(&inode);
- if (scan)
- ext2fs_close_inode_scan(scan);
- return retval;
-}
-
/* --------------------------------------------------------------------
*
* Resize processing, phase 1.
@@ -760,8 +441,7 @@ retry:
/*
* Reallocate the group descriptors as necessary.
*/
- if (EXT2_DESC_SIZE(old_fs->super) == EXT2_DESC_SIZE(fs->super) &&
- old_fs->desc_blocks != fs->desc_blocks) {
+ if (old_fs->desc_blocks != fs->desc_blocks) {
retval = ext2fs_resize_mem(old_fs->desc_blocks *
fs->blocksize,
fs->desc_blocks * fs->blocksize,
@@ -780,11 +460,8 @@ retry:
* number of descriptor blocks, then adjust
* s_reserved_gdt_blocks if possible to avoid needing to move
* the inode table either now or in the future.
- *
- * Note: If we're converting to 64bit mode, we did this earlier.
*/
- if (EXT2_DESC_SIZE(old_fs->super) == EXT2_DESC_SIZE(fs->super))
- adjust_reserved_gdt_blocks(old_fs, fs);
+ adjust_reserved_gdt_blocks(old_fs, fs);

if ((fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
(fs->super->s_first_meta_bg > fs->desc_blocks)) {
@@ -1313,9 +990,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
if (retval)
goto errout;

- if (EXT2_DESC_SIZE(rfs->old_fs->super) ==
- EXT2_DESC_SIZE(rfs->new_fs->super) &&
- old_blocks == new_blocks) {
+ if (old_blocks == new_blocks) {
retval = 0;
goto errout;
}
--
2.1.4


2015-08-07 10:51:45

by Jan Kara

[permalink] [raw]
Subject: [PATCH 05/19] resize2fs: Use ext2fs_allocate_group_table2()

Signed-off-by: Jan Kara <[email protected]>
---
resize/resize2fs.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 02f7d754d316..9d21cebe16aa 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -1448,8 +1448,10 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
ext2fs_block_bitmap_loc(fs, i))
continue;

- retval = ext2fs_allocate_group_table(fs, i,
- rfs->reserve_blocks);
+ retval = ext2fs_allocate_group_table2(fs, i,
+ rfs->reserve_blocks,
+ EXT2FS_ALLOC_TABLE_UPDATE_STATS |
+ EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP);
if (retval)
goto errout;

@@ -1459,7 +1461,6 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
*/
if (ext2fs_block_bitmap_loc(old_fs, i) !=
(blk = ext2fs_block_bitmap_loc(fs, i))) {
- ext2fs_block_alloc_stats2(fs, blk, +1);
if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk))
ext2fs_mark_block_bitmap2(rfs->move_blocks,
@@ -1467,7 +1468,6 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
}
if (ext2fs_inode_bitmap_loc(old_fs, i) !=
(blk = ext2fs_inode_bitmap_loc(fs, i))) {
- ext2fs_block_alloc_stats2(fs, blk, +1);
if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk))
ext2fs_mark_block_bitmap2(rfs->move_blocks,
@@ -1493,7 +1493,6 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
*/
for (blk = ext2fs_inode_table_loc(fs, i), j=0;
j < fs->inode_blocks_per_group ; j++, blk++) {
- ext2fs_block_alloc_stats2(fs, blk, +1);
if (ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk))
ext2fs_mark_block_bitmap2(rfs->move_blocks,
--
2.1.4


2015-08-07 11:03:38

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

On Fri 07-08-15 13:58:22, Alexey Lyashkov wrote:
> May you don’t use a magic constants in code? It hard to maintain latter.

I guess you mean the default number of reserved inodes. Yes, I guess I can
create a define for that...

Honza

> > On Aug 7, 2015, at 1:51 PM, Jan Kara <[email protected]> wrote:
> >
> > From: Jan Kara <[email protected]>
> >
> > We ran out of reserved inodes so bump the default number of reserved
> > inodes to 64 to get some breathing space. Otherwise we have to do a full
> > fs scan when increasing number of reserved inodes when some feature
> > needing another reserved inode is enabled. This consumes 13.5 KB on a
> > filesystem which is negligible these days.
> >
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > lib/ext2fs/initialize.c | 2 +-
> > misc/mke2fs.8.in | 8 ++++----
> > misc/mke2fs.conf.5.in | 7 ++++---
> > 3 files changed, 9 insertions(+), 8 deletions(-)
> >
> > diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
> > index 75fbf8ee0061..0ecf4606ce48 100644
> > --- a/lib/ext2fs/initialize.c
> > +++ b/lib/ext2fs/initialize.c
> > @@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,
> >
> > set_field(s_rev_level, EXT2_GOOD_OLD_REV);
> > if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
> > - set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
> > + set_field(s_first_ino, 64);
> > set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
> > if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
> > int extra_isize = sizeof(struct ext2_inode_large) -
> > diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> > index 520a07185f9f..6eef1d97bfbf 100644
> > --- a/misc/mke2fs.8.in
> > +++ b/misc/mke2fs.8.in
> > @@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
> > @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> > .TP
> > .BI reserved_inodes= number
> > -Specify the number of inodes reserved for system files. This number must be
> > -at least 10. Currently 10 is enough but future features may require additional
> > -reserved inodes. Reserving more inodes after file system is created requires
> > -full file system scan so it can take a long time.
> > +Specify the number of inodes reserved for system files. This number must be at
> > +least 10, default is 64. Currently 10 is enough but future features may require
> > +additional reserved inodes. Reserving more inodes after file system is created
> > +requires full file system scan so it can take a long time.
> > .RE
> > .TP
> > .BI \-f " fragment-size"
> > diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> > index 06ca9e4eabc4..b35767bfedf2 100644
> > --- a/misc/mke2fs.conf.5.in
> > +++ b/misc/mke2fs.conf.5.in
> > @@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
> > .TP
> > .I reserved_inodes
> > This relation specifies the default number of inodes reserved for system files.
> > -The number must be at least 10. Currently 10 is enough but future features may
> > -require additional reserved inodes. Reserving more inodes after file system is
> > -created requires full file system scan so it can take a long time.
> > +The number must be at least 10, default is 64. Currently 10 is enough but
> > +future features may require additional reserved inodes. Reserving more inodes
> > +after file system is created requires full file system scan so it can take a
> > +long time.
> > .TP
> > .I undo_dir
> > This relation specifies the directory where the undo file should be
> > --
> > 2.1.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-07 10:51:45

by Jan Kara

[permalink] [raw]
Subject: [PATCH 07/19] ext2fs: Provide helper for wiping resize inode

Provide a helper function ext2fs_wipe_resize_inode() which clears resize
inode and use it from resize2fs.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/res_gdt.c | 34 ++++++++++++++++++++++++++++++++++
resize/resize2fs.c | 26 ++------------------------
3 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3eda8d1f4ad3..0b8d1f6f22b1 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1588,6 +1588,7 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,

/* res_gdt.c */
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+extern errcode_t ext2fs_wipe_resize_inode(ext2_filsys fs);

/*sha256.c */
#define EXT2FS_SHA256_LENGTH 32
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index 46db61c5cf0d..5a44f5676df4 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -238,3 +238,37 @@ out_free:
return retval;
}

+/* Clear resize inode so that it can be recreated */
+errcode_t ext2fs_wipe_resize_inode(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (retval)
+ goto errout;
+
+ ext2fs_iblk_set(fs, &inode, 1);
+
+ retval = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (retval)
+ goto errout;
+
+ /* Check resize inode is sane */
+ if (!inode.i_block[EXT2_DIND_BLOCK]) {
+ retval = EINVAL;
+ goto errout;
+ }
+
+ retval = ext2fs_zero_blocks2(fs, inode.i_block[EXT2_DIND_BLOCK], 1,
+ NULL, NULL);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_create_resize_inode(fs);
+ if (retval)
+ goto errout;
+
+errout:
+ return retval;
+}
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 9d21cebe16aa..a8ba8adcfb86 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -2535,34 +2535,12 @@ static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
*/
static errcode_t fix_resize_inode(ext2_filsys fs)
{
- struct ext2_inode inode;
errcode_t retval;

- if (!(fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INODE))
+ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE))
return 0;

- retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
- if (retval) goto errout;
-
- ext2fs_iblk_set(fs, &inode, 1);
-
- retval = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
- if (retval) goto errout;
-
- if (!inode.i_block[EXT2_DIND_BLOCK]) {
- /*
- * Avoid zeroing out block #0; that's rude. This
- * should never happen anyway since the filesystem
- * should be fsck'ed and we assume it is consistent.
- */
- fprintf(stderr, "%s",
- _("Should never happen: resize inode corrupt!\n"));
- exit(1);
- }

2015-08-07 10:51:45

by Jan Kara

[permalink] [raw]
Subject: [PATCH 06/19] ext2fs: Make ext2fs_reserve_super_and_bgd() clear block_uninit flag

Currently ext2fs_reserve_super_and_bgd() does not change group
descriptor flag showing whether a particular group block bitmap is
initialized or not. Make sure to clear this flag when modifying group's
block bitmap.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/alloc_sb.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/lib/ext2fs/alloc_sb.c b/lib/ext2fs/alloc_sb.c
index 8788c00973df..9100ba29ef13 100644
--- a/lib/ext2fs/alloc_sb.c
+++ b/lib/ext2fs/alloc_sb.c
@@ -52,6 +52,11 @@ int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
ext2fs_super_and_bgd_loc2(fs, group, &super_blk,
&old_desc_blk, &new_desc_blk, &used_blks);

+ if (used_blks && bmap == fs->block_map) {
+ ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, group);
+ }
+
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
old_desc_blocks = fs->super->s_first_meta_bg;
else
--
2.1.4


2015-08-07 10:51:42

by Jan Kara

[permalink] [raw]
Subject: [PATCH 03/19] ext2fs: Add pointer to allocator private data into ext2_filsys

From: Jan Kara <[email protected]>

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/ext2fs.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 613f0d1bcc91..13cbe567b19c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -279,6 +279,8 @@ struct struct_ext2_filsys {

io_channel journal_io;
char *journal_name;
+ /* Private data for use by get_alloc_block() function */
+ void *alloc_data;
};

#if EXT2_FLAT_INCLUDES
--
2.1.4


2015-08-07 10:51:42

by Jan Kara

[permalink] [raw]
Subject: [PATCH 01/19] ext2fs: Move function to initialize uninitialized bitmaps to libext2fs

From: Jan Kara <[email protected]>

We will need to initialize uninitialized bitmaps both from resize2fs and
tune2fs. Move the function that does this to libext2fs so that code can
be shared.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/bitops.h | 1 +
lib/ext2fs/gen_bitmap64.c | 33 +++++++++++++++++++++++++++++++++
resize/resize2fs.c | 38 ++------------------------------------
3 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h
index bc596087d2a7..0699a00fcec4 100644
--- a/lib/ext2fs/bitops.h
+++ b/lib/ext2fs/bitops.h
@@ -211,6 +211,7 @@ extern errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitma
extern errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 start, __u64 end,
__u64 *out);
+extern void ext2fs_init_uninit_block_bitmaps(ext2_filsys fs);

/*
* The inline routines themselves...
diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index 3fc734981c18..34122cfa8901 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -905,3 +905,36 @@ errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,

return ENOENT;
}
+
+/*
+ * Initialize all unintialized block bitmaps
+ */
+void ext2fs_init_uninit_block_bitmaps(ext2_filsys fs)
+{
+ blk64_t blk, lblk;
+ dgrp_t g;
+ int i;
+
+ if (!ext2fs_has_group_desc_csum(fs))
+ return;
+
+ for (g = 0; g < fs->group_desc_count; g++) {
+ if (!(ext2fs_bg_flags_test(fs, g, EXT2_BG_BLOCK_UNINIT)))
+ continue;
+
+ blk = ext2fs_group_first_block2(fs, g);
+ lblk = ext2fs_group_last_block2(fs, g);
+ ext2fs_unmark_block_bitmap_range2(fs->block_map, blk,
+ lblk - blk + 1);
+
+ ext2fs_reserve_super_and_bgd(fs, g, fs->block_map);
+ ext2fs_mark_block_bitmap2(fs->block_map,
+ ext2fs_block_bitmap_loc(fs, g));
+ ext2fs_mark_block_bitmap2(fs->block_map,
+ ext2fs_inode_bitmap_loc(fs, g));
+ for (i = 0, blk = ext2fs_inode_table_loc(fs, g);
+ i < (unsigned int) fs->inode_blocks_per_group;
+ i++, blk++)
+ ext2fs_mark_block_bitmap2(fs->block_map, blk);
+ }
+}
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index 041ff75029b2..02f7d754d316 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -41,7 +41,6 @@
#define RESIZE2FS_DEBUG
#endif

-static void fix_uninit_block_bitmaps(ext2_filsys fs);
static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size);
static errcode_t blocks_to_move(ext2_resize_t rfs);
static errcode_t block_mover(ext2_resize_t rfs);
@@ -119,7 +118,7 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
ext2fs_flush(fs);

init_resource_track(&rtrack, "fix_uninit_block_bitmaps 1", fs->io);
- fix_uninit_block_bitmaps(fs);
+ ext2fs_init_uninit_block_bitmaps(fs);
print_resource_track(rfs, &rtrack, fs->io);
retval = ext2fs_dup_handle(fs, &rfs->new_fs);
if (retval)
@@ -150,7 +149,7 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
print_resource_track(rfs, &rtrack, fs->io);

init_resource_track(&rtrack, "fix_uninit_block_bitmaps 2", fs->io);
- fix_uninit_block_bitmaps(rfs->new_fs);
+ ext2fs_init_uninit_block_bitmaps(rfs->new_fs);
print_resource_track(rfs, &rtrack, fs->io);
/* Clear the block bitmap uninit flag for the last block group */
ext2fs_bg_flags_clear(rfs->new_fs, rfs->new_fs->group_desc_count - 1,
@@ -571,39 +570,6 @@ out:
return retval;
}

-/*
- * Clean up the bitmaps for unitialized bitmaps
- */
-static void fix_uninit_block_bitmaps(ext2_filsys fs)
-{
- blk64_t blk, lblk;
- dgrp_t g;
- int i;
-
- if (!ext2fs_has_group_desc_csum(fs))
- return;
-
- for (g=0; g < fs->group_desc_count; g++) {
- if (!(ext2fs_bg_flags_test(fs, g, EXT2_BG_BLOCK_UNINIT)))
- continue;
-
- blk = ext2fs_group_first_block2(fs, g);
- lblk = ext2fs_group_last_block2(fs, g);
- ext2fs_unmark_block_bitmap_range2(fs->block_map, blk,
- lblk - blk + 1);
-
- ext2fs_reserve_super_and_bgd(fs, g, fs->block_map);
- ext2fs_mark_block_bitmap2(fs->block_map,
- ext2fs_block_bitmap_loc(fs, g));
- ext2fs_mark_block_bitmap2(fs->block_map,
- ext2fs_inode_bitmap_loc(fs, g));
- for (i = 0, blk = ext2fs_inode_table_loc(fs, g);
- i < (unsigned int) fs->inode_blocks_per_group;
- i++, blk++)
- ext2fs_mark_block_bitmap2(fs->block_map, blk);
- }
-}

2015-08-07 10:51:42

by Jan Kara

[permalink] [raw]
Subject: [PATCH 04/19] ext2fs: Implement ext2fs_allocate_group_table2()

Implement ext2fs_allocate_group_table2() which marks blocks also in
fs->block_map unconditionally and which always updates allocation
statistics.

Signed-off-by: Jan Kara <[email protected]>
---
lib/ext2fs/alloc_tables.c | 93 ++++++++++++++++++++++++++++++++++-------------
lib/ext2fs/ext2fs.h | 7 ++++
2 files changed, 74 insertions(+), 26 deletions(-)

diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c
index 3e1952fa63e8..9eedd7712be9 100644
--- a/lib/ext2fs/alloc_tables.c
+++ b/lib/ext2fs/alloc_tables.c
@@ -81,19 +81,59 @@ static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk,
return first_free;
}

-errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
- ext2fs_block_bitmap bmap)
+static void update_block_bitmap_stats(ext2_filsys fs, blk64_t blk, int len,
+ unsigned long flags)
+{
+ if (flags & EXT2FS_ALLOC_TABLE_UPDATE_STATS) {
+ ext2fs_block_alloc_stats_range(fs, blk, len, +1);
+ if (!(flags & EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP))
+ ext2fs_unmark_block_bitmap_range2(fs->block_map, blk,
+ len);
+ } else if (flags & EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP) {
+ dgrp_t g, end;
+
+ ext2fs_mark_block_bitmap_range2(fs->block_map, blk, len);
+ /*
+ * It's an overkill to assume more than two groups can be
+ * touched but the code is easy to understand this way.
+ */
+ end = ext2fs_group_of_blk2(fs, blk + len - 1);
+ for (g = ext2fs_group_of_blk2(fs, blk); g <= end; g++) {
+ if (ext2fs_bg_flags_test(fs, g, EXT2_BG_BLOCK_UNINIT)) {
+ ext2fs_bg_flags_clear(fs, g,
+ EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, g);
+ }
+ }
+ }
+}
+
+/*
+ * Allocate group metadata (bitmaps, inode table) if not present. If
+ * reserve_bmap is set, we use that as a bitmap of blocks free for allocation
+ * (and update used blocks there as well as in fs->block_map).
+ */
+errcode_t ext2fs_allocate_group_table2(ext2_filsys fs, dgrp_t group,
+ ext2fs_block_bitmap reserve_bmap,
+ unsigned long flags)
{
errcode_t retval;
blk64_t group_blk, start_blk, last_blk, new_blk;
dgrp_t last_grp = 0;
int rem_grps = 0, flexbg_size = 0, table_offset = 0;
+ ext2fs_block_bitmap bmap;

group_blk = ext2fs_group_first_block2(fs, group);
last_blk = ext2fs_group_last_block2(fs, group);

- if (!bmap)
+ if (reserve_bmap == fs->block_map)
+ reserve_bmap = NULL;
+
+ if (!reserve_bmap) {
bmap = fs->block_map;
+ flags |= EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP;
+ } else
+ bmap = reserve_bmap;

if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
EXT4_FEATURE_INCOMPAT_FLEX_BG) &&
@@ -153,15 +193,10 @@ errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
last_blk, 1, bmap, &new_blk);
if (retval)
return retval;
- ext2fs_mark_block_bitmap2(bmap, new_blk);
ext2fs_block_bitmap_loc_set(fs, group, new_blk);
- if (flexbg_size) {
- dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk);
- ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1);
- ext2fs_free_blocks_count_add(fs->super, -1);
- ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT);
- ext2fs_group_desc_csum_set(fs, gr);
- }
+ update_block_bitmap_stats(fs, new_blk, 1, flags);
+ if (reserve_bmap)
+ ext2fs_mark_block_bitmap2(bmap, new_blk);
}

if (flexbg_size) {
@@ -186,15 +221,10 @@ errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
last_blk, 1, bmap, &new_blk);
if (retval)
return retval;
- ext2fs_mark_block_bitmap2(bmap, new_blk);
ext2fs_inode_bitmap_loc_set(fs, group, new_blk);
- if (flexbg_size) {
- dgrp_t gr = ext2fs_group_of_blk2(fs, new_blk);
- ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1);
- ext2fs_free_blocks_count_add(fs->super, -1);
- ext2fs_bg_flags_clear(fs, gr, EXT2_BG_BLOCK_UNINIT);
- ext2fs_group_desc_csum_set(fs, gr);
- }
+ update_block_bitmap_stats(fs, new_blk, 1, flags);
+ if (reserve_bmap)
+ ext2fs_mark_block_bitmap2(bmap, new_blk);
}

/*
@@ -223,18 +253,29 @@ errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
bmap, &new_blk);
if (retval)
return retval;
- if (flexbg_size)
- ext2fs_block_alloc_stats_range(fs, new_blk,
- fs->inode_blocks_per_group, +1);
- else
- ext2fs_mark_block_bitmap_range2(fs->block_map,
- new_blk, fs->inode_blocks_per_group);
ext2fs_inode_table_loc_set(fs, group, new_blk);
+ update_block_bitmap_stats(fs, new_blk,
+ fs->inode_blocks_per_group, flags);
+ if (reserve_bmap)
+ ext2fs_mark_block_bitmap_range2(bmap,
+ new_blk, fs->inode_blocks_per_group);
}
ext2fs_group_desc_csum_set(fs, group);
return 0;
}

+errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+ ext2fs_block_bitmap bmap)
+{
+ unsigned long flags = 0;
+
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_FLEX_BG))
+ flags |= EXT2FS_ALLOC_TABLE_UPDATE_STATS;
+
+ return ext2fs_allocate_group_table2(fs, group, bmap, flags);
+}
+
errcode_t ext2fs_allocate_tables(ext2_filsys fs)
{
errcode_t retval;
@@ -248,7 +289,7 @@ errcode_t ext2fs_allocate_tables(ext2_filsys fs)
for (i = 0; i < fs->group_desc_count; i++) {
if (fs->progress_ops && fs->progress_ops->update)
(fs->progress_ops->update)(fs, &progress, i);
- retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
+ retval = ext2fs_allocate_group_table(fs, i, 0);
if (retval)
return retval;
}
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 13cbe567b19c..3eda8d1f4ad3 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -713,6 +713,13 @@ void ext2fs_block_alloc_stats_range(ext2_filsys fs, blk64_t blk,
extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
ext2fs_block_bitmap bmap);
+/* Update allocation statistics */
+#define EXT2FS_ALLOC_TABLE_UPDATE_STATS 0x0001
+/* Set fs->block_map in addition to provided bmap */
+#define EXT2FS_ALLOC_TABLE_SET_BLOCK_BITMAP 0x0002
+extern errcode_t ext2fs_allocate_group_table2(ext2_filsys fs, dgrp_t group,
+ ext2fs_block_bitmap bmap,
+ unsigned long flags);

/* badblocks.c */
extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size);
--
2.1.4


2015-08-07 15:32:53

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH 10/19] tune2fs: Implement setting and disabling of 64-bit feature

On Fri, Aug 07, 2015 at 12:51:20PM +0200, Jan Kara wrote:
> Currently resize2fs is used to toggle 64-bit feature. However tune2fs
> should logically handle that as toggling any other feature. Since we
> have now factored out block moving code from resize2fs it is relatively
> easy to implement toggling of the feature within tune2fs.
>
> For now we leave the same functionality also in resize2fs for testing.
> Later we can make resize2fs call tune2fs for backward compatibility.

There hasn't been a release of e2fsprogs with 64bit conversion support in
resize2fs, so you can fix the conversion tests to use tune2fs and remove the
-b/-B switches from resize2fs entirely.

--D

>
> Signed-off-by: Jan Kara <[email protected]>
> ---
> misc/tune2fs.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 345 insertions(+), 22 deletions(-)
>
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index d2e8b20f64e2..d6bfad3374d1 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -447,10 +447,327 @@ static void request_fsck_afterwards(ext2_filsys fs)
> printf("%s", _("(and reboot afterwards!)\n"));
> }
>
> -static void convert_64bit(ext2_filsys fs, int direction)
> +static errcode_t fix_resize_inode(ext2_filsys fs)
> {
> - if (!direction)
> - return;
> + errcode_t retval;
> +
> + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE))
> + return 0;
> +
> + retval = ext2fs_wipe_resize_inode(fs);
> + if (retval)
> + return retval;
> +
> + return ext2fs_create_resize_inode(fs);
> +}
> +
> +/*
> + * Make blocks that are needed for new group metadata unused and write there
> + * new group descriptors
> + */
> +static errcode_t move_bg_metadata(ext2_filsys fs, ext2_filsys new_fs)
> +{
> + dgrp_t i;
> + blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
> + ext2fs_block_bitmap old_map = NULL, new_map = NULL;
> + int old, new;
> + errcode_t retval;
> + int cluster_ratio;
> + int need_new_block = 0;
> +
> + retval = ext2fs_allocate_block_bitmap(fs, "old map", &old_map);
> + if (retval)
> + goto out;
> +
> + retval = ext2fs_allocate_block_bitmap(fs, "new map", &new_map);
> + if (retval)
> + goto out;
> +
> + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
> + EXT2_FEATURE_INCOMPAT_META_BG)) {
> + old_desc_blocks = fs->super->s_first_meta_bg;
> + new_desc_blocks = new_fs->super->s_first_meta_bg;
> + } else {
> + old_desc_blocks = fs->desc_blocks +
> + fs->super->s_reserved_gdt_blocks;
> + new_desc_blocks = new_fs->desc_blocks +
> + new_fs->super->s_reserved_gdt_blocks;
> + }
> +
> + /* Construct bitmaps of super/descriptor blocks in old and new fs */
> + for (i = 0; i < fs->group_desc_count; i++) {
> + retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
> + if (retval)
> + goto out;
> + if (b)
> + ext2fs_mark_block_bitmap2(old_map, b);
> + for (j = 0; c != 0 && j < old_desc_blocks; j++)
> + ext2fs_mark_block_bitmap2(old_map, c + j);
> + if (d)
> + ext2fs_mark_block_bitmap2(old_map, d);
> +
> + retval = ext2fs_super_and_bgd_loc2(new_fs, i, &b, &c, &d, NULL);
> + if (retval)
> + goto out;
> + if (b)
> + ext2fs_mark_block_bitmap2(new_map, b);
> + for (j = 0; c != 0 && j < new_desc_blocks; j++)
> + ext2fs_mark_block_bitmap2(new_map, c + j);
> + if (d)
> + ext2fs_mark_block_bitmap2(new_map, d);
> + }
> +
> + cluster_ratio = EXT2FS_CLUSTER_RATIO(new_fs);
> +
> + /* Find changes in block allocations for bg metadata */
> + for (b = EXT2FS_B2C(fs, fs->super->s_first_data_block);
> + b < ext2fs_blocks_count(new_fs->super);
> + b += cluster_ratio) {
> + old = ext2fs_test_block_bitmap2(old_map, b);
> + new = ext2fs_test_block_bitmap2(new_map, b);
> +
> + if (old && !new) {
> + /* mark old_map, unmark new_map */
> + if (cluster_ratio == 1)
> + ext2fs_block_alloc_stats2(new_fs, b, -1);
> + } else if (!old && new)
> + need_new_block = 1; /* unmark old_map, mark new_map */
> + else {
> + ext2fs_unmark_block_bitmap2(old_map, b);
> + ext2fs_unmark_block_bitmap2(new_map, b);
> + }
> + }
> +
> + /*
> + * new_map now shows blocks that have been newly allocated.
> + * old_map now shows blocks that have been newly freed.
> + */
> +
> + if (need_new_block) {
> + /*
> + * Now relocate metadata & data conflicting with new descriptor
> + * blocks
> + */
> + retval = ext2fs_move_blocks(new_fs, new_map, old_map);
> + if (retval)
> + goto out;
> +
> + /* Now mark blocks for new group descriptors as used */
> + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> + b += cluster_ratio) {
> + if (ext2fs_test_block_bitmap2(new_map, b) &&
> + !ext2fs_test_block_bitmap2(new_fs->block_map, b))
> + ext2fs_block_alloc_stats2(new_fs, b, +1);
> + }
> + }
> +
> + if (cluster_ratio > 1) {
> + /* Free unused clusters */
> + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> + b += cluster_ratio)
> + if (ext2fs_test_block_bitmap2(old_map, b))
> + ext2fs_block_alloc_stats2(new_fs, b, -1);
> + }
> +
> + /* Now update resize inode */
> + retval = fix_resize_inode(new_fs);
> +out:
> + if (old_map)
> + ext2fs_free_block_bitmap(old_map);
> + if (new_map)
> + ext2fs_free_block_bitmap(new_map);
> + return retval;
> +}
> +
> +
> +/* Toggle 64bit mode */
> +static errcode_t resize_group_descriptors(ext2_filsys *fs_p, int direction)
> +{
> + void *o, *n, *new_group_desc;
> + dgrp_t i;
> + int copy_size;
> + errcode_t retval;
> + ext2_filsys new_fs = NULL;
> + ext2_filsys fs = *fs_p;
> +
> + retval = ext2fs_dup_handle(fs, &new_fs);
> + if (retval)
> + return retval;
> +
> + if (direction < 0) {
> + new_fs->super->s_feature_incompat &=
> + ~EXT4_FEATURE_INCOMPAT_64BIT;
> + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
> + } else {
> + new_fs->super->s_feature_incompat |=
> + EXT4_FEATURE_INCOMPAT_64BIT;
> + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
> + }
> +
> + o = new_fs->group_desc;
> + new_fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
> + EXT2_DESC_PER_BLOCK(new_fs->super));
> + retval = ext2fs_get_arrayzero(new_fs->desc_blocks,
> + fs->blocksize, &new_group_desc);
> + if (retval)
> + goto out;
> +
> + n = new_group_desc;
> +
> + if (EXT2_DESC_SIZE(fs->super) <= EXT2_DESC_SIZE(new_fs->super))
> + copy_size = EXT2_DESC_SIZE(fs->super);
> + else
> + copy_size = EXT2_DESC_SIZE(new_fs->super);
> + for (i = 0; i < fs->group_desc_count; i++) {
> + memcpy(n, o, copy_size);
> + n += EXT2_DESC_SIZE(new_fs->super);
> + o += EXT2_DESC_SIZE(fs->super);
> + }
> +
> + ext2fs_free_mem(&new_fs->group_desc);
> + new_fs->group_desc = new_group_desc;
> +
> + for (i = 0; i < fs->group_desc_count; i++)
> + ext2fs_group_desc_csum_set(new_fs, i);
> +
> + /*
> + * Update number of reserved blocks so that we need to alloc / free as
> + * few blocks as possible
> + */
> + if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
> + (fs->desc_blocks != new_fs->desc_blocks)) {
> + int new;
> +
> + new = ((int) new_fs->super->s_reserved_gdt_blocks) +
> + (fs->desc_blocks - new_fs->desc_blocks);
> + if (new < 0)
> + new = 0;
> + if (new > (int) fs->blocksize / 4)
> + new = fs->blocksize / 4;
> + new_fs->super->s_reserved_gdt_blocks = new;
> + }
> +
> + retval = move_bg_metadata(fs, new_fs);
> +
> + /* Return the modified filesystem */
> + new_fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> + ext2fs_mark_super_dirty(new_fs);
> + ext2fs_free(fs);
> + *fs_p = new_fs;
> + new_fs = NULL;
> +out:
> + if (new_fs)
> + ext2fs_free(new_fs);
> + return retval;
> +}
> +
> +/* Zero out the high bits of extent fields */
> +static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
> + struct ext2_inode *inode)
> +{
> + ext2_extent_handle_t handle;
> + struct ext2fs_extent extent;
> + int op = EXT2_EXTENT_ROOT;
> + errcode_t errcode;
> +
> + if (!(inode->i_flags & EXT4_EXTENTS_FL))
> + return 0;
> +
> + errcode = ext2fs_extent_open(fs, ino, &handle);
> + if (errcode)
> + return errcode;
> +
> + while (1) {
> + errcode = ext2fs_extent_get(handle, op, &extent);
> + if (errcode)
> + break;
> +
> + op = EXT2_EXTENT_NEXT_SIB;
> +
> + if (extent.e_pblk > (1ULL << 32)) {
> + extent.e_pblk &= (1ULL << 32) - 1;
> + errcode = ext2fs_extent_replace(handle, 0, &extent);
> + if (errcode)
> + break;
> + }
> + }
> +
> + /* Ok if we run off the end */
> + if (errcode == EXT2_ET_EXTENT_NO_NEXT)
> + errcode = 0;
> + ext2fs_extent_free(handle);
> + return errcode;
> +}
> +
> +/* Zero out the high bits of inodes. */
> +static errcode_t zero_high_bits_in_inodes(ext2_filsys fs)
> +{
> + int length = EXT2_INODE_SIZE(fs->super);
> + struct ext2_inode *inode = NULL;
> + ext2_inode_scan scan = NULL;
> + errcode_t retval;
> + ext2_ino_t ino;
> +
> + if (fs->super->s_creator_os != EXT2_OS_LINUX)
> + return 0;
> +
> + retval = ext2fs_open_inode_scan(fs, 0, &scan);
> + if (retval)
> + return retval;
> +
> + 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))
> + continue;
> +
> + /*
> + * Here's how we deal with high block number fields:
> + *
> + * - i_size_high has been been written out with i_size_lo
> + * since the ext2 days, so no conversion is needed.
> + *
> + * - i_blocks_hi is guarded by both the huge_file feature and
> + * inode flags and has always been written out with
> + * i_blocks_lo if the feature is set. The field is only
> + * ever read if both feature and inode flag are set, so
> + * we don't need to zero it now.
> + *
> + * - i_file_acl_high can be uninitialized, so zero it if
> + * it isn't already.
> + */
> + if (inode->osd2.linux2.l_i_file_acl_high) {
> + inode->osd2.linux2.l_i_file_acl_high = 0;
> + retval = ext2fs_write_inode_full(fs, ino, inode,
> + length);
> + if (retval)
> + goto out;
> + }
> +
> + retval = zero_high_bits_in_extents(fs, ino, inode);
> + if (retval)
> + goto out;
> + } while (ino);
> +
> +out:
> + if (inode)
> + ext2fs_free_mem(&inode);
> + if (scan)
> + ext2fs_close_inode_scan(scan);
> + return retval;
> +}
> +
> +static errcode_t convert_64bit(ext2_filsys *fs_p, int direction)
> +{
> + ext2_filsys fs = *fs_p;
> + errcode_t retval;
>
> /*
> * Is resize2fs going to demand a fsck run? Might as well tell the
> @@ -459,21 +776,25 @@ static void convert_64bit(ext2_filsys fs, int direction)
> 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))
> + 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);
> + return 0;
> + }
>
> - 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"));
> + /*
> + * Read bitmaps now since we are definitely going to need them and they
> + * get properly duplicated when creating new fs handle in
> + * resize_group_descriptors()
> + */
> + retval = ext2fs_read_bitmaps(fs);
> + if (retval)
> + return retval;
> +
> + retval = resize_group_descriptors(fs_p, direction);
> + if (retval)
> + return retval;
> +
> + return zero_high_bits_in_inodes(*fs_p);
> }
>
> /* Rewrite extents */
> @@ -1253,9 +1574,9 @@ mmp_error:
> }
>
> /*
> - * 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.
> + * Handle 64-bit conversion. We return the feature to the original
> + * value for now until the conversion really takes place. Otherwise
> + * some helper macros get confused.
> */
> if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> EXT4_FEATURE_INCOMPAT_64BIT)) {
> @@ -1264,8 +1585,8 @@ mmp_error:
> "while mounted!\n"));
> exit(1);
> }
> - sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> feature_64bit = 1;
> + sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> }
> if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
> EXT4_FEATURE_INCOMPAT_64BIT)) {
> @@ -1274,8 +1595,8 @@ mmp_error:
> "while mounted!\n"));
> exit(1);
> }
> - sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> feature_64bit = -1;
> + sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> }
>
> if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> @@ -3080,6 +3401,9 @@ retry_open:
> }
> }
>
> + if (feature_64bit)
> + convert_64bit(&fs, feature_64bit);
> +
> if (rewrite_checksums)
> rewrite_metadata_checksums(fs);
>
> @@ -3113,6 +3437,5 @@ closefs:
> exit(1);
> }
>
> - convert_64bit(fs, feature_64bit);
> return (ext2fs_close_free(&fs) ? 1 : 0);
> }
> --
> 2.1.4
>

2015-08-07 15:37:29

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH 11/19] mke2fs: Allow specifying number of reserved inodes

On Fri, Aug 07, 2015 at 12:51:21PM +0200, Jan Kara wrote:
> From: Jan Kara <[email protected]>
>
> Add option to specify number of reserved inodes in the created
> filesystem.
>
> Signed-off-by: Jan Kara <[email protected]>
> ---
> misc/mke2fs.8.in | 6 ++++++
> misc/mke2fs.c | 40 +++++++++++++++++++++++++++++++++++++++-
> misc/mke2fs.conf.5.in | 6 ++++++
> 3 files changed, 51 insertions(+), 1 deletion(-)
>
> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> index 40c40d3ed065..520a07185f9f 100644
> --- a/misc/mke2fs.8.in
> +++ b/misc/mke2fs.8.in
> @@ -384,6 +384,12 @@ Do not attempt to discard blocks at mkfs time.
> @[email protected] quota
> @QUOTA_MAN_COMMENT@feature is set. Without this extended option, the default
> @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> +.TP
> +.BI reserved_inodes= number
> +Specify the number of inodes reserved for system files. This number must be
> +at least 10. Currently 10 is enough but future features may require additional
> +reserved inodes. Reserving more inodes after file system is created requires
> +full file system scan so it can take a long time.

Perhaps:

"...requires a full file system scan, which can take a long time."

The "it" in "it can take" could be confused as referring to the process of
reserving more inodes (vs. moving inodes around after creation, which is what
takes a long time).

Same comment applies to the manpage at the bottom of the patch.

--D

> .RE
> .TP
> .BI \-f " fragment-size"
> diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> index 78b1252d8519..d61d1a332e67 100644
> --- a/misc/mke2fs.c
> +++ b/misc/mke2fs.c
> @@ -1024,6 +1024,34 @@ static void parse_extended_opts(struct ext2_super_block *param,
> r_usage++;
> continue;
> }
> + } else if (!strcmp(token, "reserved_inodes")) {
> + unsigned int reserved_inodes;
> +
> + if (!arg) {
> + r_usage++;
> + badopt = token;
> + continue;
> + }
> + reserved_inodes = strtoul(arg, &p, 0);
> + if (*p) {
> + fprintf(stderr,
> + _("Invalid number of reserved inodes "
> + "%s\n"),
> + arg);
> + r_usage++;
> + continue;
> + }
> + /* Ino 0 is invalid so bump by 1... */
> + reserved_inodes++;
> + if (reserved_inodes < EXT2_GOOD_OLD_FIRST_INO) {
> + fprintf(stderr,
> + _("Too few reserved inodes "
> + "%s (must be at least %u)\n"),
> + arg, EXT2_GOOD_OLD_FIRST_INO - 1);
> + r_usage++;
> + continue;
> + }
> + param->s_first_ino = reserved_inodes;
> } else {
> r_usage++;
> badopt = token;
> @@ -1049,7 +1077,8 @@ static void parse_extended_opts(struct ext2_super_block *param,
> "\ttest_fs\n"
> "\tdiscard\n"
> "\tnodiscard\n"
> - "\tquotatype=<usr OR grp>\n\n"),
> + "\tquotatype=<usr OR grp>\n"
> + "\treserved_inodes=<number of reserved inodes>\n\n"),
> badopt ? badopt : "");
> free(buf);
> exit(1);
> @@ -2422,6 +2451,15 @@ profile_error:
> exit(1);
> }
>
> + /* Count with one more inode for lost+found */
> + if (fs_param.s_first_ino >= fs_param.s_inodes_count + 1) {
> + com_err(program_name, 0, _("asked for more reserved inodes than filesystem has "
> + "available (%u >= %u)\n"),
> + (unsigned int)fs_param.s_first_ino,
> + (unsigned int)fs_param.s_inodes_count + 1);
> + exit(1);
> + }
> +
> /*
> * Calculate number of blocks to reserve
> */
> diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> index ad6c11b3cb7b..06ca9e4eabc4 100644
> --- a/misc/mke2fs.conf.5.in
> +++ b/misc/mke2fs.conf.5.in
> @@ -195,6 +195,12 @@ specify one on the command line, and the filesystem-type
> specific section of the configuration file does not specify a default
> reserved ratio. This value can be a floating point number.
> .TP
> +.I reserved_inodes
> +This relation specifies the default number of inodes reserved for system files.
> +The number must be at least 10. Currently 10 is enough but future features may
> +require additional reserved inodes. Reserving more inodes after file system is
> +created requires full file system scan so it can take a long time.
> +.TP
> .I undo_dir
> This relation specifies the directory where the undo file should be
> stored. It can be overridden via the
> --
> 2.1.4
>

2015-08-07 15:42:29

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH 10/19] tune2fs: Implement setting and disabling of 64-bit feature

On Fri, Aug 07, 2015 at 08:32:47AM -0700, Darrick J. Wong wrote:
> On Fri, Aug 07, 2015 at 12:51:20PM +0200, Jan Kara wrote:
> > Currently resize2fs is used to toggle 64-bit feature. However tune2fs
> > should logically handle that as toggling any other feature. Since we
> > have now factored out block moving code from resize2fs it is relatively
> > easy to implement toggling of the feature within tune2fs.
> >
> > For now we leave the same functionality also in resize2fs for testing.
> > Later we can make resize2fs call tune2fs for backward compatibility.
>
> There hasn't been a release of e2fsprogs with 64bit conversion support in
> resize2fs, so you can fix the conversion tests to use tune2fs and remove the
> -b/-B switches from resize2fs entirely.

Oh, I didn't notice that you /do/ remove the resize2fs part later on in the
patch set. Sorry for the noise about that.

I think there are still tests that need to be fixed up.

--D

>
> --D
>
> >
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > misc/tune2fs.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
> > 1 file changed, 345 insertions(+), 22 deletions(-)
> >
> > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > index d2e8b20f64e2..d6bfad3374d1 100644
> > --- a/misc/tune2fs.c
> > +++ b/misc/tune2fs.c
> > @@ -447,10 +447,327 @@ static void request_fsck_afterwards(ext2_filsys fs)
> > printf("%s", _("(and reboot afterwards!)\n"));
> > }
> >
> > -static void convert_64bit(ext2_filsys fs, int direction)
> > +static errcode_t fix_resize_inode(ext2_filsys fs)
> > {
> > - if (!direction)
> > - return;
> > + errcode_t retval;
> > +
> > + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE))
> > + return 0;
> > +
> > + retval = ext2fs_wipe_resize_inode(fs);
> > + if (retval)
> > + return retval;
> > +
> > + return ext2fs_create_resize_inode(fs);
> > +}
> > +
> > +/*
> > + * Make blocks that are needed for new group metadata unused and write there
> > + * new group descriptors
> > + */
> > +static errcode_t move_bg_metadata(ext2_filsys fs, ext2_filsys new_fs)
> > +{
> > + dgrp_t i;
> > + blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
> > + ext2fs_block_bitmap old_map = NULL, new_map = NULL;
> > + int old, new;
> > + errcode_t retval;
> > + int cluster_ratio;
> > + int need_new_block = 0;
> > +
> > + retval = ext2fs_allocate_block_bitmap(fs, "old map", &old_map);
> > + if (retval)
> > + goto out;
> > +
> > + retval = ext2fs_allocate_block_bitmap(fs, "new map", &new_map);
> > + if (retval)
> > + goto out;
> > +
> > + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
> > + EXT2_FEATURE_INCOMPAT_META_BG)) {
> > + old_desc_blocks = fs->super->s_first_meta_bg;
> > + new_desc_blocks = new_fs->super->s_first_meta_bg;
> > + } else {
> > + old_desc_blocks = fs->desc_blocks +
> > + fs->super->s_reserved_gdt_blocks;
> > + new_desc_blocks = new_fs->desc_blocks +
> > + new_fs->super->s_reserved_gdt_blocks;
> > + }
> > +
> > + /* Construct bitmaps of super/descriptor blocks in old and new fs */
> > + for (i = 0; i < fs->group_desc_count; i++) {
> > + retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
> > + if (retval)
> > + goto out;
> > + if (b)
> > + ext2fs_mark_block_bitmap2(old_map, b);
> > + for (j = 0; c != 0 && j < old_desc_blocks; j++)
> > + ext2fs_mark_block_bitmap2(old_map, c + j);
> > + if (d)
> > + ext2fs_mark_block_bitmap2(old_map, d);
> > +
> > + retval = ext2fs_super_and_bgd_loc2(new_fs, i, &b, &c, &d, NULL);
> > + if (retval)
> > + goto out;
> > + if (b)
> > + ext2fs_mark_block_bitmap2(new_map, b);
> > + for (j = 0; c != 0 && j < new_desc_blocks; j++)
> > + ext2fs_mark_block_bitmap2(new_map, c + j);
> > + if (d)
> > + ext2fs_mark_block_bitmap2(new_map, d);
> > + }
> > +
> > + cluster_ratio = EXT2FS_CLUSTER_RATIO(new_fs);
> > +
> > + /* Find changes in block allocations for bg metadata */
> > + for (b = EXT2FS_B2C(fs, fs->super->s_first_data_block);
> > + b < ext2fs_blocks_count(new_fs->super);
> > + b += cluster_ratio) {
> > + old = ext2fs_test_block_bitmap2(old_map, b);
> > + new = ext2fs_test_block_bitmap2(new_map, b);
> > +
> > + if (old && !new) {
> > + /* mark old_map, unmark new_map */
> > + if (cluster_ratio == 1)
> > + ext2fs_block_alloc_stats2(new_fs, b, -1);
> > + } else if (!old && new)
> > + need_new_block = 1; /* unmark old_map, mark new_map */
> > + else {
> > + ext2fs_unmark_block_bitmap2(old_map, b);
> > + ext2fs_unmark_block_bitmap2(new_map, b);
> > + }
> > + }
> > +
> > + /*
> > + * new_map now shows blocks that have been newly allocated.
> > + * old_map now shows blocks that have been newly freed.
> > + */
> > +
> > + if (need_new_block) {
> > + /*
> > + * Now relocate metadata & data conflicting with new descriptor
> > + * blocks
> > + */
> > + retval = ext2fs_move_blocks(new_fs, new_map, old_map);
> > + if (retval)
> > + goto out;
> > +
> > + /* Now mark blocks for new group descriptors as used */
> > + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> > + b += cluster_ratio) {
> > + if (ext2fs_test_block_bitmap2(new_map, b) &&
> > + !ext2fs_test_block_bitmap2(new_fs->block_map, b))
> > + ext2fs_block_alloc_stats2(new_fs, b, +1);
> > + }
> > + }
> > +
> > + if (cluster_ratio > 1) {
> > + /* Free unused clusters */
> > + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> > + b += cluster_ratio)
> > + if (ext2fs_test_block_bitmap2(old_map, b))
> > + ext2fs_block_alloc_stats2(new_fs, b, -1);
> > + }
> > +
> > + /* Now update resize inode */
> > + retval = fix_resize_inode(new_fs);
> > +out:
> > + if (old_map)
> > + ext2fs_free_block_bitmap(old_map);
> > + if (new_map)
> > + ext2fs_free_block_bitmap(new_map);
> > + return retval;
> > +}
> > +
> > +
> > +/* Toggle 64bit mode */
> > +static errcode_t resize_group_descriptors(ext2_filsys *fs_p, int direction)
> > +{
> > + void *o, *n, *new_group_desc;
> > + dgrp_t i;
> > + int copy_size;
> > + errcode_t retval;
> > + ext2_filsys new_fs = NULL;
> > + ext2_filsys fs = *fs_p;
> > +
> > + retval = ext2fs_dup_handle(fs, &new_fs);
> > + if (retval)
> > + return retval;
> > +
> > + if (direction < 0) {
> > + new_fs->super->s_feature_incompat &=
> > + ~EXT4_FEATURE_INCOMPAT_64BIT;
> > + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
> > + } else {
> > + new_fs->super->s_feature_incompat |=
> > + EXT4_FEATURE_INCOMPAT_64BIT;
> > + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
> > + }
> > +
> > + o = new_fs->group_desc;
> > + new_fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
> > + EXT2_DESC_PER_BLOCK(new_fs->super));
> > + retval = ext2fs_get_arrayzero(new_fs->desc_blocks,
> > + fs->blocksize, &new_group_desc);
> > + if (retval)
> > + goto out;
> > +
> > + n = new_group_desc;
> > +
> > + if (EXT2_DESC_SIZE(fs->super) <= EXT2_DESC_SIZE(new_fs->super))
> > + copy_size = EXT2_DESC_SIZE(fs->super);
> > + else
> > + copy_size = EXT2_DESC_SIZE(new_fs->super);
> > + for (i = 0; i < fs->group_desc_count; i++) {
> > + memcpy(n, o, copy_size);
> > + n += EXT2_DESC_SIZE(new_fs->super);
> > + o += EXT2_DESC_SIZE(fs->super);
> > + }
> > +
> > + ext2fs_free_mem(&new_fs->group_desc);
> > + new_fs->group_desc = new_group_desc;
> > +
> > + for (i = 0; i < fs->group_desc_count; i++)
> > + ext2fs_group_desc_csum_set(new_fs, i);
> > +
> > + /*
> > + * Update number of reserved blocks so that we need to alloc / free as
> > + * few blocks as possible
> > + */
> > + if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
> > + (fs->desc_blocks != new_fs->desc_blocks)) {
> > + int new;
> > +
> > + new = ((int) new_fs->super->s_reserved_gdt_blocks) +
> > + (fs->desc_blocks - new_fs->desc_blocks);
> > + if (new < 0)
> > + new = 0;
> > + if (new > (int) fs->blocksize / 4)
> > + new = fs->blocksize / 4;
> > + new_fs->super->s_reserved_gdt_blocks = new;
> > + }
> > +
> > + retval = move_bg_metadata(fs, new_fs);
> > +
> > + /* Return the modified filesystem */
> > + new_fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> > + ext2fs_mark_super_dirty(new_fs);
> > + ext2fs_free(fs);
> > + *fs_p = new_fs;
> > + new_fs = NULL;
> > +out:
> > + if (new_fs)
> > + ext2fs_free(new_fs);
> > + return retval;
> > +}
> > +
> > +/* Zero out the high bits of extent fields */
> > +static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
> > + struct ext2_inode *inode)
> > +{
> > + ext2_extent_handle_t handle;
> > + struct ext2fs_extent extent;
> > + int op = EXT2_EXTENT_ROOT;
> > + errcode_t errcode;
> > +
> > + if (!(inode->i_flags & EXT4_EXTENTS_FL))
> > + return 0;
> > +
> > + errcode = ext2fs_extent_open(fs, ino, &handle);
> > + if (errcode)
> > + return errcode;
> > +
> > + while (1) {
> > + errcode = ext2fs_extent_get(handle, op, &extent);
> > + if (errcode)
> > + break;
> > +
> > + op = EXT2_EXTENT_NEXT_SIB;
> > +
> > + if (extent.e_pblk > (1ULL << 32)) {
> > + extent.e_pblk &= (1ULL << 32) - 1;
> > + errcode = ext2fs_extent_replace(handle, 0, &extent);
> > + if (errcode)
> > + break;
> > + }
> > + }
> > +
> > + /* Ok if we run off the end */
> > + if (errcode == EXT2_ET_EXTENT_NO_NEXT)
> > + errcode = 0;
> > + ext2fs_extent_free(handle);
> > + return errcode;
> > +}
> > +
> > +/* Zero out the high bits of inodes. */
> > +static errcode_t zero_high_bits_in_inodes(ext2_filsys fs)
> > +{
> > + int length = EXT2_INODE_SIZE(fs->super);
> > + struct ext2_inode *inode = NULL;
> > + ext2_inode_scan scan = NULL;
> > + errcode_t retval;
> > + ext2_ino_t ino;
> > +
> > + if (fs->super->s_creator_os != EXT2_OS_LINUX)
> > + return 0;
> > +
> > + retval = ext2fs_open_inode_scan(fs, 0, &scan);
> > + if (retval)
> > + return retval;
> > +
> > + 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))
> > + continue;
> > +
> > + /*
> > + * Here's how we deal with high block number fields:
> > + *
> > + * - i_size_high has been been written out with i_size_lo
> > + * since the ext2 days, so no conversion is needed.
> > + *
> > + * - i_blocks_hi is guarded by both the huge_file feature and
> > + * inode flags and has always been written out with
> > + * i_blocks_lo if the feature is set. The field is only
> > + * ever read if both feature and inode flag are set, so
> > + * we don't need to zero it now.
> > + *
> > + * - i_file_acl_high can be uninitialized, so zero it if
> > + * it isn't already.
> > + */
> > + if (inode->osd2.linux2.l_i_file_acl_high) {
> > + inode->osd2.linux2.l_i_file_acl_high = 0;
> > + retval = ext2fs_write_inode_full(fs, ino, inode,
> > + length);
> > + if (retval)
> > + goto out;
> > + }
> > +
> > + retval = zero_high_bits_in_extents(fs, ino, inode);
> > + if (retval)
> > + goto out;
> > + } while (ino);
> > +
> > +out:
> > + if (inode)
> > + ext2fs_free_mem(&inode);
> > + if (scan)
> > + ext2fs_close_inode_scan(scan);
> > + return retval;
> > +}
> > +
> > +static errcode_t convert_64bit(ext2_filsys *fs_p, int direction)
> > +{
> > + ext2_filsys fs = *fs_p;
> > + errcode_t retval;
> >
> > /*
> > * Is resize2fs going to demand a fsck run? Might as well tell the
> > @@ -459,21 +776,25 @@ static void convert_64bit(ext2_filsys fs, int direction)
> > 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))
> > + 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);
> > + return 0;
> > + }
> >
> > - 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"));
> > + /*
> > + * Read bitmaps now since we are definitely going to need them and they
> > + * get properly duplicated when creating new fs handle in
> > + * resize_group_descriptors()
> > + */
> > + retval = ext2fs_read_bitmaps(fs);
> > + if (retval)
> > + return retval;
> > +
> > + retval = resize_group_descriptors(fs_p, direction);
> > + if (retval)
> > + return retval;
> > +
> > + return zero_high_bits_in_inodes(*fs_p);
> > }
> >
> > /* Rewrite extents */
> > @@ -1253,9 +1574,9 @@ mmp_error:
> > }
> >
> > /*
> > - * 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.
> > + * Handle 64-bit conversion. We return the feature to the original
> > + * value for now until the conversion really takes place. Otherwise
> > + * some helper macros get confused.
> > */
> > if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> > EXT4_FEATURE_INCOMPAT_64BIT)) {
> > @@ -1264,8 +1585,8 @@ mmp_error:
> > "while mounted!\n"));
> > exit(1);
> > }
> > - sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> > feature_64bit = 1;
> > + sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> > }
> > if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
> > EXT4_FEATURE_INCOMPAT_64BIT)) {
> > @@ -1274,8 +1595,8 @@ mmp_error:
> > "while mounted!\n"));
> > exit(1);
> > }
> > - sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> > feature_64bit = -1;
> > + sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> > }
> >
> > if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> > @@ -3080,6 +3401,9 @@ retry_open:
> > }
> > }
> >
> > + if (feature_64bit)
> > + convert_64bit(&fs, feature_64bit);
> > +
> > if (rewrite_checksums)
> > rewrite_metadata_checksums(fs);
> >
> > @@ -3113,6 +3437,5 @@ closefs:
> > exit(1);
> > }
> >
> > - convert_64bit(fs, feature_64bit);
> > return (ext2fs_close_free(&fs) ? 1 : 0);
> > }
> > --
> > 2.1.4
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2015-08-07 15:55:44

by Darrick J. Wong

[permalink] [raw]
Subject: Re: [PATCH 08/19] ext2fs: Implement block moving in libext2fs

On Fri, Aug 07, 2015 at 12:51:18PM +0200, Jan Kara wrote:
> From: Jan Kara <[email protected]>
>
> Signed-off-by: Jan Kara <[email protected]>

<snip>

> diff --git a/lib/ext2fs/extent_map.c b/lib/ext2fs/extent_map.c
> new file mode 100644
> index 000000000000..702172b24e2b
> --- /dev/null
> +++ b/lib/ext2fs/extent_map.c
> @@ -0,0 +1,233 @@
> +/*
> + * extent.c --- ext2 extent mapping abstraction
> + *
> + * This abstraction is used to provide a compact way of representing a
> + * translation table, for moving multiple contiguous ranges (extents)
> + * of blocks or inodes.
> + *
> + * Copyright (C) 1997, 1998 by Theodore Ts'o and
> + * PowerQuest, Inc.
> + *
> + * Copyright (C) 1999, 2000 by Theosore Ts'o

Ted should probably weigh in on this amazing coincidence in contributors :)
but this I suspect is a spelling error waiting a long time to be fixed.

> + *
> + * %Begin-Header%

<snip>

> diff --git a/lib/ext2fs/move.h b/lib/ext2fs/move.h
> new file mode 100644
> index 000000000000..8d66aa039ec0
> --- /dev/null
> +++ b/lib/ext2fs/move.h
> @@ -0,0 +1,23 @@
> +#ifndef _EXT2FS_MOVE_H
> +#define _EXT2FS_MOVE_H
> +
> +#include "ext2fs.h"
> +
> +typedef struct _ext2_map_extent *ext2_map_extent;
> +
> +/* extent_map.c */
> +extern int ext2fs_extent_table_empty(ext2_map_extent extent);
> +extern errcode_t ext2fs_create_extent_table(ext2_map_extent *ret_extent,
> + __u64 size);
> +extern void ext2fs_free_extent_table(ext2_map_extent extent);
> +extern errcode_t ext2fs_add_extent_entry(ext2_map_extent extent,
> + __u64 old_loc, __u64 new_loc);
> +extern __u64 ext2fs_extent_translate(ext2_map_extent extent, __u64 old_loc);
> +extern errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
> + __u64 *new_loc, __u64 *size);
> +
> +/* move.c */
> +errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap move_map,
> + ext2fs_block_bitmap reuse_map);
> +
> +#endif

Hmm... are these functions intended for use by client programs? I'm accustomed
to looking in ext2fs.h for such functions.

--D

> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2015-08-07 18:12:04

by Alexey Lyahkov

[permalink] [raw]
Subject: Re: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

It’s correct - i mean about it.
Same define may used on ext4 code to verify inode number correctness.


> On Aug 7, 2015, at 2:03 PM, Jan Kara <[email protected]> wrote:
>
> On Fri 07-08-15 13:58:22, Alexey Lyashkov wrote:
>> May you don’t use a magic constants in code? It hard to maintain latter.
>
> I guess you mean the default number of reserved inodes. Yes, I guess I can
> create a define for that...
>
> Honza
>
>>> On Aug 7, 2015, at 1:51 PM, Jan Kara <[email protected]> wrote:
>>>
>>> From: Jan Kara <[email protected]>
>>>
>>> We ran out of reserved inodes so bump the default number of reserved
>>> inodes to 64 to get some breathing space. Otherwise we have to do a full
>>> fs scan when increasing number of reserved inodes when some feature
>>> needing another reserved inode is enabled. This consumes 13.5 KB on a
>>> filesystem which is negligible these days.
>>>
>>> Signed-off-by: Jan Kara <[email protected]>
>>> ---
>>> lib/ext2fs/initialize.c | 2 +-
>>> misc/mke2fs.8.in | 8 ++++----
>>> misc/mke2fs.conf.5.in | 7 ++++---
>>> 3 files changed, 9 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
>>> index 75fbf8ee0061..0ecf4606ce48 100644
>>> --- a/lib/ext2fs/initialize.c
>>> +++ b/lib/ext2fs/initialize.c
>>> @@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,
>>>
>>> set_field(s_rev_level, EXT2_GOOD_OLD_REV);
>>> if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
>>> - set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
>>> + set_field(s_first_ino, 64);
>>> set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
>>> if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
>>> int extra_isize = sizeof(struct ext2_inode_large) -
>>> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
>>> index 520a07185f9f..6eef1d97bfbf 100644
>>> --- a/misc/mke2fs.8.in
>>> +++ b/misc/mke2fs.8.in
>>> @@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
>>> @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
>>> .TP
>>> .BI reserved_inodes= number
>>> -Specify the number of inodes reserved for system files. This number must be
>>> -at least 10. Currently 10 is enough but future features may require additional
>>> -reserved inodes. Reserving more inodes after file system is created requires
>>> -full file system scan so it can take a long time.
>>> +Specify the number of inodes reserved for system files. This number must be at
>>> +least 10, default is 64. Currently 10 is enough but future features may require
>>> +additional reserved inodes. Reserving more inodes after file system is created
>>> +requires full file system scan so it can take a long time.
>>> .RE
>>> .TP
>>> .BI \-f " fragment-size"
>>> diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
>>> index 06ca9e4eabc4..b35767bfedf2 100644
>>> --- a/misc/mke2fs.conf.5.in
>>> +++ b/misc/mke2fs.conf.5.in
>>> @@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
>>> .TP
>>> .I reserved_inodes
>>> This relation specifies the default number of inodes reserved for system files.
>>> -The number must be at least 10. Currently 10 is enough but future features may
>>> -require additional reserved inodes. Reserving more inodes after file system is
>>> -created requires full file system scan so it can take a long time.
>>> +The number must be at least 10, default is 64. Currently 10 is enough but
>>> +future features may require additional reserved inodes. Reserving more inodes
>>> +after file system is created requires full file system scan so it can take a
>>> +long time.
>>> .TP
>>> .I undo_dir
>>> This relation specifies the directory where the undo file should be
>>> --
>>> 2.1.4
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
>>> the body of a message to [email protected]
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
> --
> Jan Kara <[email protected]>
> SUSE Labs, CR

2015-08-07 18:58:52

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH 13/19] tune2fs: Add support for changing number of reserved inodes

On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
>
> Signed-off-by: Jan Kara <[email protected]>
> ---
> misc/tune2fs.8.in | 5 +++
> misc/tune2fs.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 107 insertions(+)
>
> diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> index 4373fc49b255..922705329112 100644
> --- a/misc/tune2fs.8.in
> +++ b/misc/tune2fs.8.in
> @@ -236,6 +236,11 @@ program.
> This superblock setting is only honored in 2.6.35+ kernels;
> and not at all by the ext2 and ext3 file system drivers.
> .TP
> +.BI reserved_inodes= number_of_reserved_inodes
> +Set the number of inodes reserved for system files. This number must be
> +at least 10. Currently 10 is enough but future features may require additional
> +reserved inodes. Reserving more inodes requires full file system scan so it
> +can take a long time.
> .B test_fs
> Set a flag in the filesystem superblock indicating that it may be
> mounted using experimental kernel code, such as the ext4dev filesystem.
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index d6bfad3374d1..d6e75fe00814 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -92,6 +92,7 @@ static char *features_cmd;
> static char *mntopts_cmd;
> static int stride, stripe_width;
> static int stride_set, stripe_width_set;
> +static unsigned long new_first_ino;
> static char *extended_cmd;
> static unsigned long new_inode_size;
> static char *ext_mount_opts;
> @@ -2298,6 +2299,34 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
> continue;
> }
> ext_mount_opts = strdup(arg);
> + } else if (!strcmp(token, "reserved_inodes")) {
> + if (!arg) {
> + r_usage++;
> + continue;
> + }
> + new_first_ino = strtoul(arg, &p, 0);
> + if (*p) {
> + fprintf(stderr,
> + _("Invalid number of reserved inodes "
> + "%s\n"),
> + arg);
> + r_usage++;
> + continue;
> + }
> + /* Ino 0 is invalid so bump by 1... */
> + new_first_ino++;
> + if (new_first_ino < EXT2_GOOD_OLD_FIRST_INO) {
> + fprintf(stderr,
> + _("Too few reserved inodes "
> + "%s (must be at least %u)\n"),
> + arg, EXT2_GOOD_OLD_FIRST_INO - 1);
> + r_usage++;
> + continue;
> + }
> + /*
> + * Here should go further feature tests to disallow
> + * admin to free used system inode
> + */

Not only should this check that the new_first_ino isn't shrinking too
small to cover in-use special inodes, it should also verify that it
isn't growing to cover in-use normal inodes (e.g. lost+found).

I guess one option might be to reserve "12" for the lost+found inode,
since virtually all filesystems have it this way (though not all),
and it would also help e2fsck in the future if it didn't need to
allocate some other inode for lost+found in case of corruption.

Cheers, Andreas

> } else
> r_usage++;
> }
> @@ -2312,6 +2341,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
> "\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"
> + "\treserved_inodes=<number of reserved inodes>\n"
> "\ttest_fs\n"
> "\t^test_fs\n"));
> free(buf);
> @@ -2986,6 +3016,75 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE])
> return 0;
> }
>
> +/* Zero range of inodes and mark them as free / used */
> +static errcode_t zero_inodes_range(ext2_filsys fs, ext2_ino_t start,
> + ext2_ino_t end, int inuse)
> +{
> + char *inode;
> + int length = EXT2_INODE_SIZE(fs->super);
> + ext2_ino_t ino;
> + errcode_t retval;
> +
> + retval = ext2fs_get_memzero(length, &inode);
> + if (retval)
> + return retval;
> +
> + for (ino = start; ino <= end; ino++) {
> + ext2fs_inode_alloc_stats(fs, ino, inuse);
> + retval = ext2fs_write_inode_full(fs, ino,
> + (struct ext2_inode *)inode,
> + length);
> + if (retval)
> + break;
> + }
> + ext2fs_free_mem(inode);
> +
> + return retval;
> +}
> +
> +static errcode_t update_reserved_inodes(ext2_filsys fs)
> +{
> + errcode_t retval = 0;
> + ext2fs_inode_bitmap imap;
> + ext2_ino_t ino, first_ino = fs->super->s_first_ino;
> +
> + if (new_first_ino == first_ino)
> + return 0;
> +
> + /* Group descriptors will need writing as well */
> + fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> +
> + /* Freeing reserved inodes is easy */
> + if (new_first_ino < first_ino) {
> + retval = zero_inodes_range(fs, new_first_ino, first_ino - 1,
> + -1);
> + if (retval)
> + return retval;
> + goto out;
> + }
> +
> + retval = ext2fs_allocate_inode_bitmap(fs, "inodes to move", &imap);
> + if (retval)
> + return retval;
> +
> + for (ino = fs->super->s_first_ino; ino < new_first_ino; ino++)
> + ext2fs_mark_inode_bitmap2(imap, ino);
> +
> + retval = ext2fs_move_inodes(fs, imap);
> + ext2fs_free_inode_bitmap(imap);
> + if (retval)
> + return retval;
> +
> + retval = zero_inodes_range(fs, first_ino, new_first_ino - 1, +1);
> + if (retval)
> + return retval;
> +out:
> + fs->super->s_first_ino = new_first_ino;
> + ext2fs_mark_super_dirty(fs);
> +
> + return 0;
> +}
> +
> int main(int argc, char **argv)
> {
> errcode_t retval;
> @@ -3404,6 +3503,9 @@ retry_open:
> if (feature_64bit)
> convert_64bit(&fs, feature_64bit);
>
> + if (new_first_ino)
> + update_reserved_inodes(fs);
> +
> if (rewrite_checksums)
> rewrite_metadata_checksums(fs);
>
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






2015-08-07 19:01:16

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH 15/19] resize2fs: Remove duplicit condition

On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
> Subject: [PATCH 15/19] resize2fs: Remove duplicit condition


Minor typo - s/duplicit/duplicate/

> Signed-off-by: Jan Kara <[email protected]>
> ---
> resize/resize2fs.c | 12 +-----------
> 1 file changed, 1 insertion(+), 11 deletions(-)
>
> diff --git a/resize/resize2fs.c b/resize/resize2fs.c
> index 9a8be30bbce1..22300836300d 100644
> --- a/resize/resize2fs.c
> +++ b/resize/resize2fs.c
> @@ -682,19 +682,9 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
> }
>
> /*
> - * If we are shrinking the number block groups, we're done and
> + * If we are not increasing the number block groups, we're done and
> * can exit now.
> */
> - if (rfs->old_fs->group_desc_count > fs->group_desc_count) {
> - retval = 0;
> - goto errout;
> - }
> -
> - /*
> - * If the number of block groups is staying the same, we're
> - * done and can exit now. (If the number block groups is
> - * shrinking, we had exited earlier.)
> - */
> if (rfs->old_fs->group_desc_count >= fs->group_desc_count) {
> retval = 0;
> goto errout;
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






2015-08-07 19:11:08

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
>
> From: Jan Kara <[email protected]>
>
> We ran out of reserved inodes so bump the default number of reserved
> inodes to 64 to get some breathing space. Otherwise we have to do a full
> fs scan when increasing number of reserved inodes when some feature
> needing another reserved inode is enabled. This consumes 13.5 KB on a
> filesystem which is negligible these days.

This patch also needs to fix all of the regression tests that will
fail because the inode numbers are different. Otherwise "make check"
will fail.

Cheers, Andreas

> Signed-off-by: Jan Kara <[email protected]>
> ---
> lib/ext2fs/initialize.c | 2 +-
> misc/mke2fs.8.in | 8 ++++----
> misc/mke2fs.conf.5.in | 7 ++++---
> 3 files changed, 9 insertions(+), 8 deletions(-)
>
> diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
> index 75fbf8ee0061..0ecf4606ce48 100644
> --- a/lib/ext2fs/initialize.c
> +++ b/lib/ext2fs/initialize.c
> @@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,
>
> set_field(s_rev_level, EXT2_GOOD_OLD_REV);
> if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
> - set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
> + set_field(s_first_ino, 64);
> set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
> if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
> int extra_isize = sizeof(struct ext2_inode_large) -
> diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> index 520a07185f9f..6eef1d97bfbf 100644
> --- a/misc/mke2fs.8.in
> +++ b/misc/mke2fs.8.in
> @@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
> @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> .TP
> .BI reserved_inodes= number
> -Specify the number of inodes reserved for system files. This number must be
> -at least 10. Currently 10 is enough but future features may require additional
> -reserved inodes. Reserving more inodes after file system is created requires
> -full file system scan so it can take a long time.
> +Specify the number of inodes reserved for system files. This number must be at
> +least 10, default is 64. Currently 10 is enough but future features may require
> +additional reserved inodes.

I'm not sure it is worthwhile to mention "currently 10 is enough",
since this should be updated every time some new inode is reserved,
but it will likely be forgotten. I don't think users really care
about this in the end.

> Reserving more inodes after file system is created
> +requires full file system scan so it can take a long time.
> .RE
> .TP
> .BI \-f " fragment-size"
> diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> index 06ca9e4eabc4..b35767bfedf2 100644
> --- a/misc/mke2fs.conf.5.in
> +++ b/misc/mke2fs.conf.5.in
> @@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
> .TP
> .I reserved_inodes
> This relation specifies the default number of inodes reserved for system files.
> -The number must be at least 10. Currently 10 is enough but future features may
> -require additional reserved inodes. Reserving more inodes after file system is
> -created requires full file system scan so it can take a long time.
> +The number must be at least 10, default is 64. Currently 10 is enough but
> +future features may require additional reserved inodes. Reserving more inodes
> +after file system is created requires full file system scan so it can take a
> +long time.
> .TP
> .I undo_dir
> This relation specifies the directory where the undo file should be
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






2015-08-08 07:45:06

by Alexey Lyahkov

[permalink] [raw]
Subject: Re: [PATCH 13/19] tune2fs: Add support for changing number of reserved inodes


> On Aug 7, 2015, at 1:51 PM, Jan Kara <[email protected]> wrote:
>
> Signed-off-by: Jan Kara <[email protected]>
> ---
>
> + new_first_ino = strtoul(arg, &p, 0);
> + if (*p) {
> + fprintf(stderr,
> + _("Invalid number of reserved inodes "
> + "%s\n"),
> + arg);
> + r_usage++;
> + continue;
> + }
>

this check invalid - you need check an errno for ERANGE also.
add check against maximal number inodes on FS is also good.

2015-08-26 15:50:05

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 11/19] mke2fs: Allow specifying number of reserved inodes

On Fri 07-08-15 08:37:22, Darrick J. Wong wrote:
> On Fri, Aug 07, 2015 at 12:51:21PM +0200, Jan Kara wrote:
> > From: Jan Kara <[email protected]>
> >
> > Add option to specify number of reserved inodes in the created
> > filesystem.
> >
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > misc/mke2fs.8.in | 6 ++++++
> > misc/mke2fs.c | 40 +++++++++++++++++++++++++++++++++++++++-
> > misc/mke2fs.conf.5.in | 6 ++++++
> > 3 files changed, 51 insertions(+), 1 deletion(-)
> >
> > diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> > index 40c40d3ed065..520a07185f9f 100644
> > --- a/misc/mke2fs.8.in
> > +++ b/misc/mke2fs.8.in
> > @@ -384,6 +384,12 @@ Do not attempt to discard blocks at mkfs time.
> > @[email protected] quota
> > @QUOTA_MAN_COMMENT@feature is set. Without this extended option, the default
> > @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> > +.TP
> > +.BI reserved_inodes= number
> > +Specify the number of inodes reserved for system files. This number must be
> > +at least 10. Currently 10 is enough but future features may require additional
> > +reserved inodes. Reserving more inodes after file system is created requires
> > +full file system scan so it can take a long time.
>
> Perhaps:
>
> "...requires a full file system scan, which can take a long time."
>
> The "it" in "it can take" could be confused as referring to the process of
> reserving more inodes (vs. moving inodes around after creation, which is what
> takes a long time).
>
> Same comment applies to the manpage at the bottom of the patch.

OK, wording changed to what you suggested. Thanks!

Honza

> > .RE
> > .TP
> > .BI \-f " fragment-size"
> > diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> > index 78b1252d8519..d61d1a332e67 100644
> > --- a/misc/mke2fs.c
> > +++ b/misc/mke2fs.c
> > @@ -1024,6 +1024,34 @@ static void parse_extended_opts(struct ext2_super_block *param,
> > r_usage++;
> > continue;
> > }
> > + } else if (!strcmp(token, "reserved_inodes")) {
> > + unsigned int reserved_inodes;
> > +
> > + if (!arg) {
> > + r_usage++;
> > + badopt = token;
> > + continue;
> > + }
> > + reserved_inodes = strtoul(arg, &p, 0);
> > + if (*p) {
> > + fprintf(stderr,
> > + _("Invalid number of reserved inodes "
> > + "%s\n"),
> > + arg);
> > + r_usage++;
> > + continue;
> > + }
> > + /* Ino 0 is invalid so bump by 1... */
> > + reserved_inodes++;
> > + if (reserved_inodes < EXT2_GOOD_OLD_FIRST_INO) {
> > + fprintf(stderr,
> > + _("Too few reserved inodes "
> > + "%s (must be at least %u)\n"),
> > + arg, EXT2_GOOD_OLD_FIRST_INO - 1);
> > + r_usage++;
> > + continue;
> > + }
> > + param->s_first_ino = reserved_inodes;
> > } else {
> > r_usage++;
> > badopt = token;
> > @@ -1049,7 +1077,8 @@ static void parse_extended_opts(struct ext2_super_block *param,
> > "\ttest_fs\n"
> > "\tdiscard\n"
> > "\tnodiscard\n"
> > - "\tquotatype=<usr OR grp>\n\n"),
> > + "\tquotatype=<usr OR grp>\n"
> > + "\treserved_inodes=<number of reserved inodes>\n\n"),
> > badopt ? badopt : "");
> > free(buf);
> > exit(1);
> > @@ -2422,6 +2451,15 @@ profile_error:
> > exit(1);
> > }
> >
> > + /* Count with one more inode for lost+found */
> > + if (fs_param.s_first_ino >= fs_param.s_inodes_count + 1) {
> > + com_err(program_name, 0, _("asked for more reserved inodes than filesystem has "
> > + "available (%u >= %u)\n"),
> > + (unsigned int)fs_param.s_first_ino,
> > + (unsigned int)fs_param.s_inodes_count + 1);
> > + exit(1);
> > + }
> > +
> > /*
> > * Calculate number of blocks to reserve
> > */
> > diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> > index ad6c11b3cb7b..06ca9e4eabc4 100644
> > --- a/misc/mke2fs.conf.5.in
> > +++ b/misc/mke2fs.conf.5.in
> > @@ -195,6 +195,12 @@ specify one on the command line, and the filesystem-type
> > specific section of the configuration file does not specify a default
> > reserved ratio. This value can be a floating point number.
> > .TP
> > +.I reserved_inodes
> > +This relation specifies the default number of inodes reserved for system files.
> > +The number must be at least 10. Currently 10 is enough but future features may
> > +require additional reserved inodes. Reserving more inodes after file system is
> > +created requires full file system scan so it can take a long time.
> > +.TP
> > .I undo_dir
> > This relation specifies the directory where the undo file should be
> > stored. It can be overridden via the
> > --
> > 2.1.4
> >
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 15:51:09

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 10/19] tune2fs: Implement setting and disabling of 64-bit feature

On Fri 07-08-15 08:42:23, Darrick J. Wong wrote:
> On Fri, Aug 07, 2015 at 08:32:47AM -0700, Darrick J. Wong wrote:
> > On Fri, Aug 07, 2015 at 12:51:20PM +0200, Jan Kara wrote:
> > > Currently resize2fs is used to toggle 64-bit feature. However tune2fs
> > > should logically handle that as toggling any other feature. Since we
> > > have now factored out block moving code from resize2fs it is relatively
> > > easy to implement toggling of the feature within tune2fs.
> > >
> > > For now we leave the same functionality also in resize2fs for testing.
> > > Later we can make resize2fs call tune2fs for backward compatibility.
> >
> > There hasn't been a release of e2fsprogs with 64bit conversion support in
> > resize2fs, so you can fix the conversion tests to use tune2fs and remove the
> > -b/-B switches from resize2fs entirely.
>
> Oh, I didn't notice that you /do/ remove the resize2fs part later on in the
> patch set. Sorry for the noise about that.
>
> I think there are still tests that need to be fixed up.

Yes, tests needed fixing up (which took me much longer than I thought
because they revealed couple of bugs in my new code ;). Now all the tests
seem to be passing.

Honza
>
> --D
>
> >
> > --D
> >
> > >
> > > Signed-off-by: Jan Kara <[email protected]>
> > > ---
> > > misc/tune2fs.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
> > > 1 file changed, 345 insertions(+), 22 deletions(-)
> > >
> > > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > > index d2e8b20f64e2..d6bfad3374d1 100644
> > > --- a/misc/tune2fs.c
> > > +++ b/misc/tune2fs.c
> > > @@ -447,10 +447,327 @@ static void request_fsck_afterwards(ext2_filsys fs)
> > > printf("%s", _("(and reboot afterwards!)\n"));
> > > }
> > >
> > > -static void convert_64bit(ext2_filsys fs, int direction)
> > > +static errcode_t fix_resize_inode(ext2_filsys fs)
> > > {
> > > - if (!direction)
> > > - return;
> > > + errcode_t retval;
> > > +
> > > + if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE))
> > > + return 0;
> > > +
> > > + retval = ext2fs_wipe_resize_inode(fs);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + return ext2fs_create_resize_inode(fs);
> > > +}
> > > +
> > > +/*
> > > + * Make blocks that are needed for new group metadata unused and write there
> > > + * new group descriptors
> > > + */
> > > +static errcode_t move_bg_metadata(ext2_filsys fs, ext2_filsys new_fs)
> > > +{
> > > + dgrp_t i;
> > > + blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
> > > + ext2fs_block_bitmap old_map = NULL, new_map = NULL;
> > > + int old, new;
> > > + errcode_t retval;
> > > + int cluster_ratio;
> > > + int need_new_block = 0;
> > > +
> > > + retval = ext2fs_allocate_block_bitmap(fs, "old map", &old_map);
> > > + if (retval)
> > > + goto out;
> > > +
> > > + retval = ext2fs_allocate_block_bitmap(fs, "new map", &new_map);
> > > + if (retval)
> > > + goto out;
> > > +
> > > + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
> > > + EXT2_FEATURE_INCOMPAT_META_BG)) {
> > > + old_desc_blocks = fs->super->s_first_meta_bg;
> > > + new_desc_blocks = new_fs->super->s_first_meta_bg;
> > > + } else {
> > > + old_desc_blocks = fs->desc_blocks +
> > > + fs->super->s_reserved_gdt_blocks;
> > > + new_desc_blocks = new_fs->desc_blocks +
> > > + new_fs->super->s_reserved_gdt_blocks;
> > > + }
> > > +
> > > + /* Construct bitmaps of super/descriptor blocks in old and new fs */
> > > + for (i = 0; i < fs->group_desc_count; i++) {
> > > + retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
> > > + if (retval)
> > > + goto out;
> > > + if (b)
> > > + ext2fs_mark_block_bitmap2(old_map, b);
> > > + for (j = 0; c != 0 && j < old_desc_blocks; j++)
> > > + ext2fs_mark_block_bitmap2(old_map, c + j);
> > > + if (d)
> > > + ext2fs_mark_block_bitmap2(old_map, d);
> > > +
> > > + retval = ext2fs_super_and_bgd_loc2(new_fs, i, &b, &c, &d, NULL);
> > > + if (retval)
> > > + goto out;
> > > + if (b)
> > > + ext2fs_mark_block_bitmap2(new_map, b);
> > > + for (j = 0; c != 0 && j < new_desc_blocks; j++)
> > > + ext2fs_mark_block_bitmap2(new_map, c + j);
> > > + if (d)
> > > + ext2fs_mark_block_bitmap2(new_map, d);
> > > + }
> > > +
> > > + cluster_ratio = EXT2FS_CLUSTER_RATIO(new_fs);
> > > +
> > > + /* Find changes in block allocations for bg metadata */
> > > + for (b = EXT2FS_B2C(fs, fs->super->s_first_data_block);
> > > + b < ext2fs_blocks_count(new_fs->super);
> > > + b += cluster_ratio) {
> > > + old = ext2fs_test_block_bitmap2(old_map, b);
> > > + new = ext2fs_test_block_bitmap2(new_map, b);
> > > +
> > > + if (old && !new) {
> > > + /* mark old_map, unmark new_map */
> > > + if (cluster_ratio == 1)
> > > + ext2fs_block_alloc_stats2(new_fs, b, -1);
> > > + } else if (!old && new)
> > > + need_new_block = 1; /* unmark old_map, mark new_map */
> > > + else {
> > > + ext2fs_unmark_block_bitmap2(old_map, b);
> > > + ext2fs_unmark_block_bitmap2(new_map, b);
> > > + }
> > > + }
> > > +
> > > + /*
> > > + * new_map now shows blocks that have been newly allocated.
> > > + * old_map now shows blocks that have been newly freed.
> > > + */
> > > +
> > > + if (need_new_block) {
> > > + /*
> > > + * Now relocate metadata & data conflicting with new descriptor
> > > + * blocks
> > > + */
> > > + retval = ext2fs_move_blocks(new_fs, new_map, old_map);
> > > + if (retval)
> > > + goto out;
> > > +
> > > + /* Now mark blocks for new group descriptors as used */
> > > + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> > > + b += cluster_ratio) {
> > > + if (ext2fs_test_block_bitmap2(new_map, b) &&
> > > + !ext2fs_test_block_bitmap2(new_fs->block_map, b))
> > > + ext2fs_block_alloc_stats2(new_fs, b, +1);
> > > + }
> > > + }
> > > +
> > > + if (cluster_ratio > 1) {
> > > + /* Free unused clusters */
> > > + for (b = 0; b < ext2fs_blocks_count(new_fs->super);
> > > + b += cluster_ratio)
> > > + if (ext2fs_test_block_bitmap2(old_map, b))
> > > + ext2fs_block_alloc_stats2(new_fs, b, -1);
> > > + }
> > > +
> > > + /* Now update resize inode */
> > > + retval = fix_resize_inode(new_fs);
> > > +out:
> > > + if (old_map)
> > > + ext2fs_free_block_bitmap(old_map);
> > > + if (new_map)
> > > + ext2fs_free_block_bitmap(new_map);
> > > + return retval;
> > > +}
> > > +
> > > +
> > > +/* Toggle 64bit mode */
> > > +static errcode_t resize_group_descriptors(ext2_filsys *fs_p, int direction)
> > > +{
> > > + void *o, *n, *new_group_desc;
> > > + dgrp_t i;
> > > + int copy_size;
> > > + errcode_t retval;
> > > + ext2_filsys new_fs = NULL;
> > > + ext2_filsys fs = *fs_p;
> > > +
> > > + retval = ext2fs_dup_handle(fs, &new_fs);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + if (direction < 0) {
> > > + new_fs->super->s_feature_incompat &=
> > > + ~EXT4_FEATURE_INCOMPAT_64BIT;
> > > + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE;
> > > + } else {
> > > + new_fs->super->s_feature_incompat |=
> > > + EXT4_FEATURE_INCOMPAT_64BIT;
> > > + new_fs->super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
> > > + }
> > > +
> > > + o = new_fs->group_desc;
> > > + new_fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
> > > + EXT2_DESC_PER_BLOCK(new_fs->super));
> > > + retval = ext2fs_get_arrayzero(new_fs->desc_blocks,
> > > + fs->blocksize, &new_group_desc);
> > > + if (retval)
> > > + goto out;
> > > +
> > > + n = new_group_desc;
> > > +
> > > + if (EXT2_DESC_SIZE(fs->super) <= EXT2_DESC_SIZE(new_fs->super))
> > > + copy_size = EXT2_DESC_SIZE(fs->super);
> > > + else
> > > + copy_size = EXT2_DESC_SIZE(new_fs->super);
> > > + for (i = 0; i < fs->group_desc_count; i++) {
> > > + memcpy(n, o, copy_size);
> > > + n += EXT2_DESC_SIZE(new_fs->super);
> > > + o += EXT2_DESC_SIZE(fs->super);
> > > + }
> > > +
> > > + ext2fs_free_mem(&new_fs->group_desc);
> > > + new_fs->group_desc = new_group_desc;
> > > +
> > > + for (i = 0; i < fs->group_desc_count; i++)
> > > + ext2fs_group_desc_csum_set(new_fs, i);
> > > +
> > > + /*
> > > + * Update number of reserved blocks so that we need to alloc / free as
> > > + * few blocks as possible
> > > + */
> > > + if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
> > > + (fs->desc_blocks != new_fs->desc_blocks)) {
> > > + int new;
> > > +
> > > + new = ((int) new_fs->super->s_reserved_gdt_blocks) +
> > > + (fs->desc_blocks - new_fs->desc_blocks);
> > > + if (new < 0)
> > > + new = 0;
> > > + if (new > (int) fs->blocksize / 4)
> > > + new = fs->blocksize / 4;
> > > + new_fs->super->s_reserved_gdt_blocks = new;
> > > + }
> > > +
> > > + retval = move_bg_metadata(fs, new_fs);
> > > +
> > > + /* Return the modified filesystem */
> > > + new_fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> > > + ext2fs_mark_super_dirty(new_fs);
> > > + ext2fs_free(fs);
> > > + *fs_p = new_fs;
> > > + new_fs = NULL;
> > > +out:
> > > + if (new_fs)
> > > + ext2fs_free(new_fs);
> > > + return retval;
> > > +}
> > > +
> > > +/* Zero out the high bits of extent fields */
> > > +static errcode_t zero_high_bits_in_extents(ext2_filsys fs, ext2_ino_t ino,
> > > + struct ext2_inode *inode)
> > > +{
> > > + ext2_extent_handle_t handle;
> > > + struct ext2fs_extent extent;
> > > + int op = EXT2_EXTENT_ROOT;
> > > + errcode_t errcode;
> > > +
> > > + if (!(inode->i_flags & EXT4_EXTENTS_FL))
> > > + return 0;
> > > +
> > > + errcode = ext2fs_extent_open(fs, ino, &handle);
> > > + if (errcode)
> > > + return errcode;
> > > +
> > > + while (1) {
> > > + errcode = ext2fs_extent_get(handle, op, &extent);
> > > + if (errcode)
> > > + break;
> > > +
> > > + op = EXT2_EXTENT_NEXT_SIB;
> > > +
> > > + if (extent.e_pblk > (1ULL << 32)) {
> > > + extent.e_pblk &= (1ULL << 32) - 1;
> > > + errcode = ext2fs_extent_replace(handle, 0, &extent);
> > > + if (errcode)
> > > + break;
> > > + }
> > > + }
> > > +
> > > + /* Ok if we run off the end */
> > > + if (errcode == EXT2_ET_EXTENT_NO_NEXT)
> > > + errcode = 0;
> > > + ext2fs_extent_free(handle);
> > > + return errcode;
> > > +}
> > > +
> > > +/* Zero out the high bits of inodes. */
> > > +static errcode_t zero_high_bits_in_inodes(ext2_filsys fs)
> > > +{
> > > + int length = EXT2_INODE_SIZE(fs->super);
> > > + struct ext2_inode *inode = NULL;
> > > + ext2_inode_scan scan = NULL;
> > > + errcode_t retval;
> > > + ext2_ino_t ino;
> > > +
> > > + if (fs->super->s_creator_os != EXT2_OS_LINUX)
> > > + return 0;
> > > +
> > > + retval = ext2fs_open_inode_scan(fs, 0, &scan);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + 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))
> > > + continue;
> > > +
> > > + /*
> > > + * Here's how we deal with high block number fields:
> > > + *
> > > + * - i_size_high has been been written out with i_size_lo
> > > + * since the ext2 days, so no conversion is needed.
> > > + *
> > > + * - i_blocks_hi is guarded by both the huge_file feature and
> > > + * inode flags and has always been written out with
> > > + * i_blocks_lo if the feature is set. The field is only
> > > + * ever read if both feature and inode flag are set, so
> > > + * we don't need to zero it now.
> > > + *
> > > + * - i_file_acl_high can be uninitialized, so zero it if
> > > + * it isn't already.
> > > + */
> > > + if (inode->osd2.linux2.l_i_file_acl_high) {
> > > + inode->osd2.linux2.l_i_file_acl_high = 0;
> > > + retval = ext2fs_write_inode_full(fs, ino, inode,
> > > + length);
> > > + if (retval)
> > > + goto out;
> > > + }
> > > +
> > > + retval = zero_high_bits_in_extents(fs, ino, inode);
> > > + if (retval)
> > > + goto out;
> > > + } while (ino);
> > > +
> > > +out:
> > > + if (inode)
> > > + ext2fs_free_mem(&inode);
> > > + if (scan)
> > > + ext2fs_close_inode_scan(scan);
> > > + return retval;
> > > +}
> > > +
> > > +static errcode_t convert_64bit(ext2_filsys *fs_p, int direction)
> > > +{
> > > + ext2_filsys fs = *fs_p;
> > > + errcode_t retval;
> > >
> > > /*
> > > * Is resize2fs going to demand a fsck run? Might as well tell the
> > > @@ -459,21 +776,25 @@ static void convert_64bit(ext2_filsys fs, int direction)
> > > 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))
> > > + 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);
> > > + return 0;
> > > + }
> > >
> > > - 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"));
> > > + /*
> > > + * Read bitmaps now since we are definitely going to need them and they
> > > + * get properly duplicated when creating new fs handle in
> > > + * resize_group_descriptors()
> > > + */
> > > + retval = ext2fs_read_bitmaps(fs);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + retval = resize_group_descriptors(fs_p, direction);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + return zero_high_bits_in_inodes(*fs_p);
> > > }
> > >
> > > /* Rewrite extents */
> > > @@ -1253,9 +1574,9 @@ mmp_error:
> > > }
> > >
> > > /*
> > > - * 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.
> > > + * Handle 64-bit conversion. We return the feature to the original
> > > + * value for now until the conversion really takes place. Otherwise
> > > + * some helper macros get confused.
> > > */
> > > if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
> > > EXT4_FEATURE_INCOMPAT_64BIT)) {
> > > @@ -1264,8 +1585,8 @@ mmp_error:
> > > "while mounted!\n"));
> > > exit(1);
> > > }
> > > - sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> > > feature_64bit = 1;
> > > + sb->s_feature_incompat &= ~EXT4_FEATURE_INCOMPAT_64BIT;
> > > }
> > > if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
> > > EXT4_FEATURE_INCOMPAT_64BIT)) {
> > > @@ -1274,8 +1595,8 @@ mmp_error:
> > > "while mounted!\n"));
> > > exit(1);
> > > }
> > > - sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> > > feature_64bit = -1;
> > > + sb->s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
> > > }
> > >
> > > if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
> > > @@ -3080,6 +3401,9 @@ retry_open:
> > > }
> > > }
> > >
> > > + if (feature_64bit)
> > > + convert_64bit(&fs, feature_64bit);
> > > +
> > > if (rewrite_checksums)
> > > rewrite_metadata_checksums(fs);
> > >
> > > @@ -3113,6 +3437,5 @@ closefs:
> > > exit(1);
> > > }
> > >
> > > - convert_64bit(fs, feature_64bit);
> > > return (ext2fs_close_free(&fs) ? 1 : 0);
> > > }
> > > --
> > > 2.1.4
> > >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 15:55:26

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 08/19] ext2fs: Implement block moving in libext2fs

On Fri 07-08-15 08:55:14, Darrick J. Wong wrote:
> On Fri, Aug 07, 2015 at 12:51:18PM +0200, Jan Kara wrote:
> > From: Jan Kara <[email protected]>
> >
> > Signed-off-by: Jan Kara <[email protected]>
>
> <snip>
>
> > diff --git a/lib/ext2fs/extent_map.c b/lib/ext2fs/extent_map.c
> > new file mode 100644
> > index 000000000000..702172b24e2b
> > --- /dev/null
> > +++ b/lib/ext2fs/extent_map.c
> > @@ -0,0 +1,233 @@
> > +/*
> > + * extent.c --- ext2 extent mapping abstraction
> > + *
> > + * This abstraction is used to provide a compact way of representing a
> > + * translation table, for moving multiple contiguous ranges (extents)
> > + * of blocks or inodes.
> > + *
> > + * Copyright (C) 1997, 1998 by Theodore Ts'o and
> > + * PowerQuest, Inc.
> > + *
> > + * Copyright (C) 1999, 2000 by Theosore Ts'o
>
> Ted should probably weigh in on this amazing coincidence in contributors :)
> but this I suspect is a spelling error waiting a long time to be fixed.

Fixed up.

> > + *
> > + * %Begin-Header%
>
> <snip>
>
> > diff --git a/lib/ext2fs/move.h b/lib/ext2fs/move.h
> > new file mode 100644
> > index 000000000000..8d66aa039ec0
> > --- /dev/null
> > +++ b/lib/ext2fs/move.h
> > @@ -0,0 +1,23 @@
> > +#ifndef _EXT2FS_MOVE_H
> > +#define _EXT2FS_MOVE_H
> > +
> > +#include "ext2fs.h"
> > +
> > +typedef struct _ext2_map_extent *ext2_map_extent;
> > +
> > +/* extent_map.c */
> > +extern int ext2fs_extent_table_empty(ext2_map_extent extent);
> > +extern errcode_t ext2fs_create_extent_table(ext2_map_extent *ret_extent,
> > + __u64 size);
> > +extern void ext2fs_free_extent_table(ext2_map_extent extent);
> > +extern errcode_t ext2fs_add_extent_entry(ext2_map_extent extent,
> > + __u64 old_loc, __u64 new_loc);
> > +extern __u64 ext2fs_extent_translate(ext2_map_extent extent, __u64 old_loc);
> > +extern errcode_t ext2fs_iterate_extent(ext2_map_extent extent, __u64 *old_loc,
> > + __u64 *new_loc, __u64 *size);
> > +
> > +/* move.c */
> > +errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap move_map,
> > + ext2fs_block_bitmap reuse_map);
> > +
> > +#endif
>
> Hmm... are these functions intended for use by client programs? I'm accustomed
> to looking in ext2fs.h for such functions.

So block moving function is definitely for use by client programs. So I'll
move that to ext2fs.h. Extent mapping functions could possibly be useful but
no interface in libext2fs needs these as an input / output so I'd consider
these internal.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 15:58:24

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 12/19] libext2fs: Bump default number of reserved inodes to 64

On Fri 07-08-15 13:11:05, Andreas Dilger wrote:
> On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
> >
> > From: Jan Kara <[email protected]>
> >
> > We ran out of reserved inodes so bump the default number of reserved
> > inodes to 64 to get some breathing space. Otherwise we have to do a full
> > fs scan when increasing number of reserved inodes when some feature
> > needing another reserved inode is enabled. This consumes 13.5 KB on a
> > filesystem which is negligible these days.
>
> This patch also needs to fix all of the regression tests that will
> fail because the inode numbers are different. Otherwise "make check"
> will fail.

Yeah, it was a bit tedious work but now all the tests are passing. Thanks
for noticing.

Honza

>
> Cheers, Andreas
>
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > lib/ext2fs/initialize.c | 2 +-
> > misc/mke2fs.8.in | 8 ++++----
> > misc/mke2fs.conf.5.in | 7 ++++---
> > 3 files changed, 9 insertions(+), 8 deletions(-)
> >
> > diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
> > index 75fbf8ee0061..0ecf4606ce48 100644
> > --- a/lib/ext2fs/initialize.c
> > +++ b/lib/ext2fs/initialize.c
> > @@ -186,7 +186,7 @@ errcode_t ext2fs_initialize(const char *name, int flags,
> >
> > set_field(s_rev_level, EXT2_GOOD_OLD_REV);
> > if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
> > - set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
> > + set_field(s_first_ino, 64);
> > set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
> > if (super->s_inode_size >= sizeof(struct ext2_inode_large)) {
> > int extra_isize = sizeof(struct ext2_inode_large) -
> > diff --git a/misc/mke2fs.8.in b/misc/mke2fs.8.in
> > index 520a07185f9f..6eef1d97bfbf 100644
> > --- a/misc/mke2fs.8.in
> > +++ b/misc/mke2fs.8.in
> > @@ -386,10 +386,10 @@ Do not attempt to discard blocks at mkfs time.
> > @QUOTA_MAN_COMMENT@behavior is to initialize both user and group quotas.
> > .TP
> > .BI reserved_inodes= number
> > -Specify the number of inodes reserved for system files. This number must be
> > -at least 10. Currently 10 is enough but future features may require additional
> > -reserved inodes. Reserving more inodes after file system is created requires
> > -full file system scan so it can take a long time.
> > +Specify the number of inodes reserved for system files. This number must be at
> > +least 10, default is 64. Currently 10 is enough but future features may require
> > +additional reserved inodes.
>
> I'm not sure it is worthwhile to mention "currently 10 is enough",
> since this should be updated every time some new inode is reserved,
> but it will likely be forgotten. I don't think users really care
> about this in the end.
>
> > Reserving more inodes after file system is created
> > +requires full file system scan so it can take a long time.
> > .RE
> > .TP
> > .BI \-f " fragment-size"
> > diff --git a/misc/mke2fs.conf.5.in b/misc/mke2fs.conf.5.in
> > index 06ca9e4eabc4..b35767bfedf2 100644
> > --- a/misc/mke2fs.conf.5.in
> > +++ b/misc/mke2fs.conf.5.in
> > @@ -197,9 +197,10 @@ reserved ratio. This value can be a floating point number.
> > .TP
> > .I reserved_inodes
> > This relation specifies the default number of inodes reserved for system files.
> > -The number must be at least 10. Currently 10 is enough but future features may
> > -require additional reserved inodes. Reserving more inodes after file system is
> > -created requires full file system scan so it can take a long time.
> > +The number must be at least 10, default is 64. Currently 10 is enough but
> > +future features may require additional reserved inodes. Reserving more inodes
> > +after file system is created requires full file system scan so it can take a
> > +long time.
> > .TP
> > .I undo_dir
> > This relation specifies the directory where the undo file should be
> > --
> > 2.1.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
> Cheers, Andreas
>
>
>
>
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 16:07:10

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 13/19] tune2fs: Add support for changing number of reserved inodes

On Sat 08-08-15 10:45:00, Alexey Lyashkov wrote:
>
> > On Aug 7, 2015, at 1:51 PM, Jan Kara <[email protected]> wrote:
> >
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> >
> > + new_first_ino = strtoul(arg, &p, 0);
> > + if (*p) {
> > + fprintf(stderr,
> > + _("Invalid number of reserved inodes "
> > + "%s\n"),
> > + arg);
> > + r_usage++;
> > + continue;
> > + }
> >
>
> this check invalid - you need check an errno for ERANGE also.
> add check against maximal number inodes on FS is also good.

Good point. Added both checks.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 16:11:50

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 13/19] tune2fs: Add support for changing number of reserved inodes

On Fri 07-08-15 12:58:49, Andreas Dilger wrote:
> On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
> >
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > misc/tune2fs.8.in | 5 +++
> > misc/tune2fs.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 107 insertions(+)
> >
> > diff --git a/misc/tune2fs.8.in b/misc/tune2fs.8.in
> > index 4373fc49b255..922705329112 100644
> > --- a/misc/tune2fs.8.in
> > +++ b/misc/tune2fs.8.in
> > @@ -236,6 +236,11 @@ program.
> > This superblock setting is only honored in 2.6.35+ kernels;
> > and not at all by the ext2 and ext3 file system drivers.
> > .TP
> > +.BI reserved_inodes= number_of_reserved_inodes
> > +Set the number of inodes reserved for system files. This number must be
> > +at least 10. Currently 10 is enough but future features may require additional
> > +reserved inodes. Reserving more inodes requires full file system scan so it
> > +can take a long time.
> > .B test_fs
> > Set a flag in the filesystem superblock indicating that it may be
> > mounted using experimental kernel code, such as the ext4dev filesystem.
> > diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> > index d6bfad3374d1..d6e75fe00814 100644
> > --- a/misc/tune2fs.c
> > +++ b/misc/tune2fs.c
> > @@ -92,6 +92,7 @@ static char *features_cmd;
> > static char *mntopts_cmd;
> > static int stride, stripe_width;
> > static int stride_set, stripe_width_set;
> > +static unsigned long new_first_ino;
> > static char *extended_cmd;
> > static unsigned long new_inode_size;
> > static char *ext_mount_opts;
> > @@ -2298,6 +2299,34 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
> > continue;
> > }
> > ext_mount_opts = strdup(arg);
> > + } else if (!strcmp(token, "reserved_inodes")) {
> > + if (!arg) {
> > + r_usage++;
> > + continue;
> > + }
> > + new_first_ino = strtoul(arg, &p, 0);
> > + if (*p) {
> > + fprintf(stderr,
> > + _("Invalid number of reserved inodes "
> > + "%s\n"),
> > + arg);
> > + r_usage++;
> > + continue;
> > + }
> > + /* Ino 0 is invalid so bump by 1... */
> > + new_first_ino++;
> > + if (new_first_ino < EXT2_GOOD_OLD_FIRST_INO) {
> > + fprintf(stderr,
> > + _("Too few reserved inodes "
> > + "%s (must be at least %u)\n"),
> > + arg, EXT2_GOOD_OLD_FIRST_INO - 1);
> > + r_usage++;
> > + continue;
> > + }
> > + /*
> > + * Here should go further feature tests to disallow
> > + * admin to free used system inode
> > + */
>
> Not only should this check that the new_first_ino isn't shrinking too
> small to cover in-use special inodes, it should also verify that it
> isn't growing to cover in-use normal inodes (e.g. lost+found).
>
> I guess one option might be to reserve "12" for the lost+found inode,
> since virtually all filesystems have it this way (though not all),
> and it would also help e2fsck in the future if it didn't need to
> allocate some other inode for lost+found in case of corruption.

We do take care to move any normal inodes that are in the way. So this
isn't an issue... That's why I had to refactor all the resize code after
all...

Honza
>
> Cheers, Andreas
>
> > } else
> > r_usage++;
> > }
> > @@ -2312,6 +2341,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
> > "\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"
> > + "\treserved_inodes=<number of reserved inodes>\n"
> > "\ttest_fs\n"
> > "\t^test_fs\n"));
> > free(buf);
> > @@ -2986,6 +3016,75 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE])
> > return 0;
> > }
> >
> > +/* Zero range of inodes and mark them as free / used */
> > +static errcode_t zero_inodes_range(ext2_filsys fs, ext2_ino_t start,
> > + ext2_ino_t end, int inuse)
> > +{
> > + char *inode;
> > + int length = EXT2_INODE_SIZE(fs->super);
> > + ext2_ino_t ino;
> > + errcode_t retval;
> > +
> > + retval = ext2fs_get_memzero(length, &inode);
> > + if (retval)
> > + return retval;
> > +
> > + for (ino = start; ino <= end; ino++) {
> > + ext2fs_inode_alloc_stats(fs, ino, inuse);
> > + retval = ext2fs_write_inode_full(fs, ino,
> > + (struct ext2_inode *)inode,
> > + length);
> > + if (retval)
> > + break;
> > + }
> > + ext2fs_free_mem(inode);
> > +
> > + return retval;
> > +}
> > +
> > +static errcode_t update_reserved_inodes(ext2_filsys fs)
> > +{
> > + errcode_t retval = 0;
> > + ext2fs_inode_bitmap imap;
> > + ext2_ino_t ino, first_ino = fs->super->s_first_ino;
> > +
> > + if (new_first_ino == first_ino)
> > + return 0;
> > +
> > + /* Group descriptors will need writing as well */
> > + fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
> > +
> > + /* Freeing reserved inodes is easy */
> > + if (new_first_ino < first_ino) {
> > + retval = zero_inodes_range(fs, new_first_ino, first_ino - 1,
> > + -1);
> > + if (retval)
> > + return retval;
> > + goto out;
> > + }
> > +
> > + retval = ext2fs_allocate_inode_bitmap(fs, "inodes to move", &imap);
> > + if (retval)
> > + return retval;
> > +
> > + for (ino = fs->super->s_first_ino; ino < new_first_ino; ino++)
> > + ext2fs_mark_inode_bitmap2(imap, ino);
> > +
> > + retval = ext2fs_move_inodes(fs, imap);
> > + ext2fs_free_inode_bitmap(imap);
> > + if (retval)
> > + return retval;
> > +
> > + retval = zero_inodes_range(fs, first_ino, new_first_ino - 1, +1);
> > + if (retval)
> > + return retval;
> > +out:
> > + fs->super->s_first_ino = new_first_ino;
> > + ext2fs_mark_super_dirty(fs);
> > +
> > + return 0;
> > +}
> > +
> > int main(int argc, char **argv)
> > {
> > errcode_t retval;
> > @@ -3404,6 +3503,9 @@ retry_open:
> > if (feature_64bit)
> > convert_64bit(&fs, feature_64bit);
> >
> > + if (new_first_ino)
> > + update_reserved_inodes(fs);
> > +
> > if (rewrite_checksums)
> > rewrite_metadata_checksums(fs);
> >
> > --
> > 2.1.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
> Cheers, Andreas
>
>
>
>
>
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2015-08-26 16:12:51

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH 15/19] resize2fs: Remove duplicit condition

On Fri 07-08-15 13:01:13, Andreas Dilger wrote:
> On Aug 7, 2015, at 4:51 AM, Jan Kara <[email protected]> wrote:
> > Subject: [PATCH 15/19] resize2fs: Remove duplicit condition
>
>
> Minor typo - s/duplicit/duplicate/

Thanks. Fixed.

Honza

>
> > Signed-off-by: Jan Kara <[email protected]>
> > ---
> > resize/resize2fs.c | 12 +-----------
> > 1 file changed, 1 insertion(+), 11 deletions(-)
> >
> > diff --git a/resize/resize2fs.c b/resize/resize2fs.c
> > index 9a8be30bbce1..22300836300d 100644
> > --- a/resize/resize2fs.c
> > +++ b/resize/resize2fs.c
> > @@ -682,19 +682,9 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
> > }
> >
> > /*
> > - * If we are shrinking the number block groups, we're done and
> > + * If we are not increasing the number block groups, we're done and
> > * can exit now.
> > */
> > - if (rfs->old_fs->group_desc_count > fs->group_desc_count) {
> > - retval = 0;
> > - goto errout;
> > - }
> > -
> > - /*
> > - * If the number of block groups is staying the same, we're
> > - * done and can exit now. (If the number block groups is
> > - * shrinking, we had exited earlier.)
> > - */
> > if (rfs->old_fs->group_desc_count >= fs->group_desc_count) {
> > retval = 0;
> > goto errout;
> > --
> > 2.1.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
> Cheers, Andreas
>
>
>
>
>
>
--
Jan Kara <[email protected]>
SUSE Labs, CR