Whamcloud - gitweb
e2fsck: do a better job of fixing i_size of inline directories
authorDarrick J. Wong <darrick.wong@oracle.com>
Sun, 10 Aug 2014 22:44:07 +0000 (18:44 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 10 Aug 2014 22:44:07 +0000 (18:44 -0400)
If we encounter a directory whose i_size != the inline data size, just
set i_size to the size of the inline data.  The pb.last_block
calculation is wrong since pb.last_block == -1, which results in
i_size being set to zero, which corrupts the directory.

Clear the inline_data inode flag if we actually /are/ setting i_size
to zero.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/pass1.c

index 53ded2e..005e9e1 100644 (file)
@@ -2856,17 +2856,21 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                if (inode->i_flags & EXT4_INLINE_DATA_FL) {
                        int flags;
                        size_t size;
+                       errcode_t err;
 
+                       size = 0;
                        flags = ctx->fs->flags;
                        ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
-                       if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size))
-                               bad_size = 5;
+                       err = ext2fs_inline_data_size(ctx->fs, pctx->ino,
+                                                     &size);
                        ctx->fs->flags = (flags &
                                          EXT2_FLAG_IGNORE_CSUM_ERRORS) |
                                         (ctx->fs->flags &
                                          ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
-                       if (size != inode->i_size)
-                               bad_size = 5;
+                       if (err || size != inode->i_size) {
+                               bad_size = 7;
+                               pctx->num = size;
+                       }
                } else if (inode->i_size & (fs->blocksize - 1))
                        bad_size = 5;
                else if (nblock > (pb.last_block + 1))
@@ -2899,12 +2903,20 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        }
        /* i_size for symlinks is checked elsewhere */
        if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
-               pctx->num = (pb.last_block+1) * fs->blocksize;
+               /* Did inline_data set pctx->num earlier? */
+               if (bad_size != 7)
+                       pctx->num = (pb.last_block + 1) * fs->blocksize;
                pctx->group = bad_size;
                if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
                        if (LINUX_S_ISDIR(inode->i_mode))
                                pctx->num &= 0xFFFFFFFFULL;
                        ext2fs_inode_size_set(fs, inode, pctx->num);
+                       if (EXT2_I_SIZE(inode) == 0 &&
+                           (inode->i_flags & EXT4_INLINE_DATA_FL)) {
+                               memset(inode->i_block, 0,
+                                      sizeof(inode->i_block));
+                               inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+                       }
                        dirty_inode++;
                }
                pctx->num = 0;