* Copyright (C) 1997, 1998 by Theodore Ts'o and
* PowerQuest, Inc.
*
- * Copyright (C) 1999, 2000 by Theosore Ts'o
+ * Copyright (C) 1999, 2000 by Theodore Ts'o
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
fs->inode_blocks_per_group);
}
-/* Some bigalloc helper macros which are more succint... */
+/* Some bigalloc helper macros which are more succinct... */
#define B2C(x) EXT2FS_B2C(fs, (x))
#define C2B(x) EXT2FS_C2B(fs, (x))
#define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
}
/*
- * Clean up the bitmaps for unitialized bitmaps
+ * Clean up the bitmaps for uninitialized bitmaps
*/
static void fix_uninit_block_bitmaps(ext2_filsys fs)
{
*/
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"),
+ fprintf(stderr, _("inodes (%llu) must be less than %u\n"),
new_inodes, ~0U);
return EXT2_ET_TOO_MANY_INODES;
}
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)
+ if (getenv("RESIZE2FS_FORCE_LAZY_ITABLE_INIT") ||
+ (!getenv("RESIZE2FS_FORCE_ITABLE_INIT") &&
+ access("/sys/fs/ext4/features/lazy_itable_init", F_OK) == 0))
lazy_itable_init = 1;
if (ext2fs_has_feature_meta_bg(fs->super))
old_desc_blocks = fs->super->s_first_meta_bg;
/*
* 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.
+ * filesystem's block allocation map.
*/
for (i = 0; i < old_fs->group_desc_count; i++)
ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
/*
* For those structures that have changed, we need to
- * do bookkeepping.
+ * do bookkeeping.
*/
if (ext2fs_block_bitmap_loc(old_fs, i) !=
(blk = ext2fs_block_bitmap_loc(fs, i))) {
return err;
}
-/* Rewrite extents */
-static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino)
+static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)),
+ errcode_t code EXT2FS_ATTR((unused)),
+ const char *fmt EXT2FS_ATTR((unused)),
+ va_list args EXT2FS_ATTR((unused)))
{
- 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;
+static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry,
+ struct ext2_ext_attr_entry *end, ext2_ino_t last_ino)
+{
+ int modified = 0;
+ ext2_ino_t new_ino;
+
+ while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (entry->e_value_inum > last_ino) {
+ new_ino = ext2fs_extent_translate(imap,
+ entry->e_value_inum);
+ entry->e_value_inum = new_ino;
+ modified = 1;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ return modified;
+}
- errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
- if (errcode)
+static int fix_ea_ibody_entries(ext2_extent imap,
+ struct ext2_inode_large *inode, int inode_size,
+ ext2_ino_t last_ino)
+{
+ struct ext2_ext_attr_entry *start, *end;
+ __u32 *ea_magic;
+
+ if (inode->i_extra_isize == 0)
+ return 0;
+
+ ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize);
+ if (*ea_magic != EXT2_EXT_ATTR_MAGIC)
+ return 0;
+
+ start = (struct ext2_ext_attr_entry *)(ea_magic + 1);
+ end = (struct ext2_ext_attr_entry *)((char *)inode + inode_size);
+
+ return fix_ea_entries(imap, start, end, last_ino);
+}
+
+static int fix_ea_block_entries(ext2_extent imap, char *block_buf,
+ unsigned int blocksize, ext2_ino_t last_ino)
+{
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *start, *end;
+
+ header = (struct ext2_ext_attr_header *)block_buf;
+ start = (struct ext2_ext_attr_entry *)(header+1);
+ end = (struct ext2_ext_attr_entry *)(block_buf + blocksize);
+
+ return fix_ea_entries(imap, start, end, last_ino);
+}
+
+/* A simple LRU cache to check recently processed blocks. */
+struct blk_cache {
+ int cursor;
+ blk64_t blks[4];
+};
+
+#define BLK_IN_CACHE(b,c) ((b) == (c).blks[0] || (b) == (c).blks[1] || \
+ (b) == (c).blks[2] || (b) == (c).blks[3])
+#define BLK_ADD_CACHE(b,c) { \
+ (c).blks[(c).cursor] = (b); \
+ (c).cursor = ((c).cursor + 1) % 4; \
+}
+
+static errcode_t fix_ea_inode_refs(ext2_resize_t rfs, struct ext2_inode *inode,
+ char *block_buf, ext2_ino_t last_ino)
+{
+ ext2_filsys fs = rfs->new_fs;
+ ext2_inode_scan scan = NULL;
+ ext2_ino_t ino;
+ int inode_size = EXT2_INODE_SIZE(fs->super);
+ blk64_t blk;
+ int modified;
+ struct blk_cache blk_cache;
+ struct ext2_ext_attr_header *header;
+ errcode_t retval;
+
+ memset(&blk_cache, 0, sizeof(blk_cache));
+
+ header = (struct ext2_ext_attr_header *)block_buf;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
goto out;
- do {
- errcode = ext2fs_extent_get_info(handle, &info);
- if (errcode)
+ while (1) {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode,
+ inode_size);
+ if (retval)
+ goto out;
+ if (!ino)
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;
- }
+ if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
+ continue; /* inode not in use */
- /* 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;
+ if (inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
+ modified = fix_ea_ibody_entries(rfs->imap,
+ (struct ext2_inode_large *)inode,
+ inode_size, last_ino);
+ if (modified) {
+ retval = ext2fs_write_inode_full(fs, ino, inode,
+ inode_size);
+ if (retval)
+ goto out;
+ }
}
- errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
- } while (errcode == 0);
+ blk = ext2fs_file_acl_block(fs, inode);
+ if (blk && !BLK_IN_CACHE(blk, blk_cache)) {
+ retval = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (retval)
+ goto out;
+ modified = fix_ea_block_entries(rfs->imap, block_buf,
+ fs->blocksize,
+ last_ino);
+ if (modified) {
+ retval = ext2fs_write_ext_attr3(fs, blk,
+ block_buf, ino);
+ if (retval)
+ goto out;
+ /*
+ * If refcount is greater than 1, we might see
+ * the same block referenced by other inodes
+ * later.
+ */
+ if (header->h_refcount > 1)
+ BLK_ADD_CACHE(blk, blk_cache);
+ }
+ }
+ }
+ retval = 0;
out:
- /* Ok if we run off the end */
- if (errcode == EXT2_ET_EXTENT_NO_NEXT)
- errcode = 0;
- ext2fs_extent_free(handle);
- return errcode;
-}
+ if (scan)
+ ext2fs_close_inode_scan(scan);
+ return retval;
-static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)),
- errcode_t code EXT2FS_ATTR((unused)),
- const char *fmt EXT2FS_ATTR((unused)),
- va_list args EXT2FS_ATTR((unused)))
-{
}
-
static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
{
struct process_block_struct pb;
char *block_buf = 0;
ext2_ino_t start_to_move;
int inode_size;
+ int update_ea_inode_refs = 0;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
pb.is_dir);
- inode->i_ctime = time(0);
+ /*
+ * i_ctime field in xattr inodes contain a portion of the ref
+ * count, do not overwrite.
+ */
+ if (inode->i_flags & EXT4_EA_INODE_FL)
+ update_ea_inode_refs = 1;
+ else
+ inode->i_ctime = time(0);
+
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
inode, inode_size);
if (retval)
if (retval)
goto errout;
- /* Rewrite extent block checksums with new inode number */
- if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) &&
- (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 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.
*/
+ rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
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) {
if (retval)
goto errout;
}
+
+ /* Fix up extent block checksums with the new inode number */
+ if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) &&
+ (inode->i_flags & EXT4_EXTENTS_FL)) {
+ retval = ext2fs_fix_extents_checksums(rfs->old_fs,
+ new_inode, NULL);
+ if (retval)
+ goto errout;
+ }
+ }
+
+ if (update_ea_inode_refs &&
+ ext2fs_has_feature_ea_inode(rfs->new_fs->super)) {
+ retval = fix_ea_inode_refs(rfs, inode, block_buf,
+ start_to_move);
+ if (retval)
+ goto errout;
}
io_channel_flush(rfs->old_fs->io);
errout:
reset_com_err_hook();
+ rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (rfs->bmap) {
ext2fs_free_extent_table(rfs->bmap);
rfs->bmap = 0;
/*
- * calcluate the minimum number of blocks the given fs can be resized to
+ * calculate the minimum number of blocks the given fs can be resized to
*/
blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
{
#endif
/*
- * if we need more group descriptors in order to accomodate our data
+ * if we need more group descriptors in order to accommodate our data
* then we need to add them here
*/
blks_needed = data_needed;