Whamcloud - gitweb
Merge branch 'maint' into next
[tools/e2fsprogs.git] / resize / resize2fs.c
index b3755f6..b59f482 100644 (file)
@@ -240,8 +240,7 @@ static void fix_uninit_block_bitmaps(ext2_filsys fs)
        dgrp_t          g;
        int             i;
 
-       if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                        EXT4_FEATURE_RO_COMPAT_GDT_CSUM)))
+       if (!ext2fs_has_group_desc_csum(fs))
                return;
 
        for (g=0; g < fs->group_desc_count; g++) {
@@ -572,8 +571,7 @@ retry:
         */
        group_block = ext2fs_group_first_block2(fs,
                                                old_fs->group_desc_count);
-       csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+       csum_flag = ext2fs_has_group_desc_csum(fs);
        if (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)
@@ -731,9 +729,7 @@ static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size)
         * supports lazy inode initialization, we can skip
         * initializing the inode table.
         */
-       if (lazy_itable_init &&
-           EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
+       if (lazy_itable_init && ext2fs_has_group_desc_csum(fs)) {
                retval = 0;
                goto errout;
        }
@@ -889,9 +885,9 @@ static void mark_fs_metablock(ext2_resize_t rfs,
                        }
                }
        }
-       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                      EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
-                  (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) {
+
+       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.
@@ -987,8 +983,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
        for (blk = ext2fs_blocks_count(fs->super);
             blk < ext2fs_blocks_count(old_fs->super); blk++) {
                g = ext2fs_group_of_blk2(fs, blk);
-               if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
-                                              EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
+               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
@@ -1461,10 +1456,12 @@ static __u64 extent_translate(ext2_filsys fs, ext2_extent extent, __u64 old_loc)
 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,
@@ -1488,11 +1485,23 @@ static int process_block(ext2_filsys fs, blk64_t        *block_nr,
 #ifdef RESIZE2FS_DEBUG
                        if (pb->rfs->flags & RESIZE_DEBUG_BMOVE)
                                printf("ino=%u, blockcnt=%lld, %llu->%llu\n",
-                                      pb->ino, blockcnt, block, new_block);
+                                      pb->old_ino, blockcnt, block,
+                                      new_block);
 #endif
                        block = new_block;
                }
        }
+
+       /*
+        * If we moved inodes and metadata_csum is enabled, we must force the
+        * extent block to be rewritten with new checksum.
+        */
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+           pb->has_extents &&
+           pb->old_ino != pb->ino)
+               ret |= BLOCK_CHANGED;
+
        if (pb->is_dir) {
                retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
                                               block, (int) blockcnt);
@@ -1532,6 +1541,46 @@ static errcode_t progress_callback(ext2_filsys fs,
        return 0;
 }
 
+static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
+                                 struct ext2_inode *inode, int *changed)
+{
+       char *buf;
+       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;
+}
+
 static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
 {
        struct process_block_struct     pb;
@@ -1542,7 +1591,6 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
        char                    *block_buf = 0;
        ext2_ino_t              start_to_move;
        blk64_t                 orig_size;
-       blk64_t                 new_block;
        int                     inode_size;
 
        if ((rfs->old_fs->group_desc_count <=
@@ -1605,37 +1653,19 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
                pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
                pb.changed = 0;
 
-               if (ext2fs_file_acl_block(rfs->old_fs, inode) && rfs->bmap) {
-                       new_block = extent_translate(rfs->old_fs, rfs->bmap,
-                               ext2fs_file_acl_block(rfs->old_fs, inode));
-                       if (new_block) {
-                               ext2fs_file_acl_block_set(rfs->old_fs, inode,
-                                                         new_block);
-                               retval = ext2fs_write_inode_full(rfs->old_fs,
-                                                           ino, inode, inode_size);
-                               if (retval) goto errout;
-                       }
-               }
-
-               if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
-                   (rfs->bmap || pb.is_dir)) {
-                       pb.ino = ino;
-                       retval = ext2fs_block_iterate3(rfs->old_fs,
-                                                      ino, 0, block_buf,
-                                                      process_block, &pb);
-                       if (retval)
-                               goto errout;
-                       if (pb.error) {
-                               retval = pb.error;
-                               goto errout;
-                       }
-               }
+               /* Remap EA block */
+               retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
+               if (retval)
+                       goto errout;
 
+               new_inode = ino;
                if (ino <= start_to_move)
-                       continue; /* Don't need to move it. */
+                       goto remap_blocks; /* Don't need to move inode. */
 
                /*
-                * Find a new 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)
@@ -1643,16 +1673,12 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
 
                ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
                                          pb.is_dir);
-               if (pb.changed) {
-                       /* Get the new version of the inode */
-                       retval = ext2fs_read_inode_full(rfs->old_fs, ino,
-                                               inode, inode_size);
-                       if (retval) goto errout;
-               }
                inode->i_ctime = time(0);
                retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
                                                inode, inode_size);
-               if (retval) goto errout;
+               if (retval)
+                       goto errout;
+               pb.changed = 0;
 
 #ifdef RESIZE2FS_DEBUG
                if (rfs->flags & RESIZE_DEBUG_INODEMAP)
@@ -1664,6 +1690,44 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
                                goto errout;
                }
                ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
+
+remap_blocks:
+               if (pb.changed)
+                       retval = ext2fs_write_inode_full(rfs->old_fs,
+                                                        new_inode,
+                                                        inode, inode_size);
+               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.
+                */
+               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;
+               }
        }
        io_channel_flush(rfs->old_fs->io);
 
@@ -1706,6 +1770,7 @@ static int check_and_change_inodes(ext2_ino_t dir,
        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);
@@ -1716,17 +1781,26 @@ static int check_and_change_inodes(ext2_ino_t dir,
                        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 0;
+               return ret;
 
        new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
 
        if (!new_inode)
-               return 0;
+               return ret;
 #ifdef RESIZE2FS_DEBUG
        if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
                printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
-                      dir, dirent->name_len&0xFF, dirent->name,
+                      dir, ext2fs_dirent_name_len(dirent), dirent->name,
                       dirent->inode, new_inode);
 #endif
 
@@ -1738,10 +1812,10 @@ static int check_and_change_inodes(ext2_ino_t dir,
                inode.i_mtime = inode.i_ctime = time(0);
                is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
                if (is->err)
-                       return DIRENT_ABORT;
+                       return ret | DIRENT_ABORT;
        }
 
-       return DIRENT_CHANGED;
+       return ret | DIRENT_CHANGED;
 }
 
 static errcode_t inode_ref_fix(ext2_resize_t rfs)
@@ -1768,9 +1842,11 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
                        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) {