]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: Add support for handling orphan file
authorJan Kara <jack@suse.cz>
Mon, 12 Jul 2021 15:43:12 +0000 (17:43 +0200)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 3 Aug 2021 18:24:57 +0000 (14:24 -0400)
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/e2fsck.h
e2fsck/pass1.c
e2fsck/pass4.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/super.c
e2fsck/unix.c

index 5704316f66f738d51c8510c0fa738a793b1cf98c..29838b9fccffa385e7a8e6e5959ae7f62908733e 100644 (file)
@@ -649,6 +649,7 @@ void sigcatcher_setup(void);
 void check_super_block(e2fsck_t ctx);
 int check_backup_super_block(e2fsck_t ctx);
 void check_resize_inode(e2fsck_t ctx);
+int check_init_orphan_file(e2fsck_t ctx);
 
 /* util.c */
 extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
index dde862a821767bd05720c8c1c7b68c69b7fca455..89b734f2fe500558486c684c0b18d088bb28852a 100644 (file)
@@ -1778,6 +1778,32 @@ void e2fsck_pass1(e2fsck_t ctx)
                                                        inode_size, "pass1");
                                failed_csum = 0;
                        }
+               } else if (ino == fs->super->s_orphan_file_inum) {
+                       ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
+                       if (ext2fs_has_feature_orphan_file(fs->super)) {
+                               if (!LINUX_S_ISREG(inode->i_mode) &&
+                                   fix_problem(ctx, PR_1_ORPHAN_FILE_BAD_MODE,
+                                               &pctx)) {
+                                       inode->i_mode = LINUX_S_IFREG;
+                                       e2fsck_write_inode(ctx, ino, inode,
+                                                          "pass1");
+                                       failed_csum = 0;
+                               }
+                               check_blocks(ctx, &pctx, block_buf, NULL);
+                               FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+                               continue;
+                       }
+                       if ((inode->i_links_count ||
+                            inode->i_blocks || inode->i_block[0]) &&
+                           fix_problem(ctx, PR_1_ORPHAN_FILE_NOT_CLEAR,
+                                       &pctx)) {
+                               memset(inode, 0, inode_size);
+                               ext2fs_icount_store(ctx->inode_link_info, ino,
+                                                   0);
+                               e2fsck_write_inode_full(ctx, ino, inode,
+                                                       inode_size, "pass1");
+                               failed_csum = 0;
+                       }
                } else if (ino < EXT2_FIRST_INODE(fs->super)) {
                        problem_t problem = 0;
 
@@ -3482,6 +3508,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        }
 
        if (ino != quota_type2inum(PRJQUOTA, fs->super) &&
+           ino != fs->super->s_orphan_file_inum &&
            (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
            !(inode->i_flags & EXT4_EA_INODE_FL)) {
                quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode,
index 8c2d2f1fca1234320fd28ffe65882a53aa168504..f41eb849e567276e41371649aa7494274753f0c6 100644 (file)
@@ -192,7 +192,7 @@ void e2fsck_pass4(e2fsck_t ctx)
                                        goto errout;
                }
                if (i == quota_type2inum(PRJQUOTA, ctx->fs->super) ||
-                   i == EXT2_BAD_INO ||
+                   i == fs->super->s_orphan_file_inum || i == EXT2_BAD_INO ||
                    (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
                        continue;
                if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, i)) ||
index 757b5d5648a8c6fc8772f0a2d522a407685ddd73..566d76d69c2d205170e2736d5671d871baa509f5 100644 (file)
@@ -526,6 +526,26 @@ static struct e2fsck_problem problem_table[] = {
             "not compatible. Resize @i should be disabled.  "),
          PROMPT_FIX, 0, 0, 0, 0 },
 
+       /* Orphan file contains holes */
+       { PR_0_ORPHAN_FILE_HOLE,
+         N_("Orphan file (@i %i) contains hole at @b %b. Terminating orphan file recovery.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file block has wrong magic */
+       { PR_0_ORPHAN_FILE_BAD_MAGIC,
+         N_("Orphan file (@i %i) @b %b contains wrong magic. Terminating orphan file recovery.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file block has wrong checksum */
+       { PR_0_ORPHAN_FILE_BAD_CHECKSUM,
+         N_("Orphan file (@i %i) @b %b contains wrong checksum. Terminating orphan file recovery.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file size isn't multiple of blocks size */
+       { PR_0_ORPHAN_FILE_WRONG_SIZE,
+         N_("Orphan file (@i %i) size is not multiple of block size. Terminating orphan file recovery.\n"),
+         PROMPT_NONE, 0 },
+
        /* Pass 1 errors */
 
        /* Pass 1: Checking inodes, blocks, and sizes */
@@ -1280,6 +1300,16 @@ static struct e2fsck_problem problem_table[] = {
          PROMPT_CLEAR_HTREE, PR_PREEN_OK, 0, 0, 0 },
 
 
+       /* Orphan file has bad mode */
+       { PR_1_ORPHAN_FILE_BAD_MODE,
+         N_("Orphan file @i %i is not regular file.  "),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Orphan file inode is not in use, but contains data */
+       { PR_1_ORPHAN_FILE_NOT_CLEAR,
+         N_("Orphan file @i %i is not in use, but contains data.  "),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
        /* Pass 1b errors */
 
        /* Pass 1B: Rescan for duplicate/bad blocks */
@@ -2259,6 +2289,56 @@ static struct e2fsck_problem problem_table[] = {
          N_("Error writing quota info for quota type %N: %m\n"),
          PROMPT_NULL, 0, 0, 0, 0 },
 
+       /* Orphan file without a journal */
+       { PR_6_ORPHAN_FILE_WITHOUT_JOURNAL,
+         N_("@S has orphan file without @j.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Orphan file truncation failed */
+       { PR_6_ORPHAN_FILE_TRUNC_FAILED,
+         N_("Failed to truncate orphan file.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Failed to initialize orphan file */
+       { PR_6_ORPHAN_FILE_CORRUPTED,
+         N_("Failed to initialize orphan file.\n"),
+         PROMPT_RECREATE, PR_PREEN_OK },
+
+       /* Cannot fix corrupted orphan file with invalid bitmaps */
+       { PR_6_ORPHAN_FILE_BITMAP_INVALID,
+         N_("Cannot fix corrupted orphan file with invalid bitmaps.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file creation failed */
+       { PR_6_ORPHAN_FILE_CREATE_FAILED,
+         N_("Failed to truncate orphan file (@i %i).\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file block contains data */
+       { PR_6_ORPHAN_BLOCK_DIRTY,
+         N_("Orphan file (@i %i) @b %b is not clean.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* orphan_present set but orphan file is empty */
+       { PR_6_ORPHAN_PRESENT_CLEAN_FILE,
+         N_("Flag orphan_present is set but orphan file is clean.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* orphan_present set but orphan_file is not */
+       { PR_6_ORPHAN_PRESENT_NO_FILE,
+         N_("Flag orphan_present is set but flag orphan_file is not.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Orphan file size isn't multiple of blocks size */
+       { PR_6_ORPHAN_FILE_WRONG_SIZE,
+         N_("Orphan file (@i %i) size is not multiple of block size.\n"),
+         PROMPT_NONE, 0 },
+
+       /* Orphan file contains holes */
+       { PR_6_ORPHAN_FILE_HOLE,
+         N_("Orphan file (@i %i) contains hole at @b %b.\n"),
+         PROMPT_NONE, 0 },
+
        { 0 }
 };
 
index 24cdcf9b90f7821f371d91260faba9f0c4447a80..7965b82bb68af5d63db99f6f7dbf3d0a73960c20 100644 (file)
@@ -288,6 +288,18 @@ struct problem_context {
 /* Meta_bg and resize_inode are not compatible, remove resize_inode*/
 #define PR_0_DISABLE_RESIZE_INODE              0x000051
 
+/* Orphan file contains holes */
+#define PR_0_ORPHAN_FILE_HOLE                  0x000052
+
+/* Orphan file block has wrong magic */
+#define PR_0_ORPHAN_FILE_BAD_MAGIC             0x000053
+
+/* Orphan file block has wrong checksum */
+#define PR_0_ORPHAN_FILE_BAD_CHECKSUM          0x000054
+
+/* Orphan file size isn't multiple of blocks size */
+#define PR_0_ORPHAN_FILE_WRONG_SIZE            0x000055
+
 /*
  * Pass 1 errors
  */
@@ -717,6 +729,15 @@ struct problem_context {
 #define PR_1_HTREE_CANNOT_SIPHASH              0x01008E
 
 
+/* Orphan file inode is not a regular file */
+#define PR_1_ORPHAN_FILE_BAD_MODE              0x01008F
+
+/* Orphan file inode is not in use, but contains data */
+#define PR_1_ORPHAN_FILE_NOT_CLEAR             0x010090
+
+/* Orphan file inode is not clear */
+#define PR_1_ORPHAN_INODE_NOT_CLEAR            0x010091
+
 /*
  * Pass 1b errors
  */
@@ -1293,6 +1314,35 @@ struct problem_context {
 /* Error updating quota information */
 #define PR_6_WRITE_QUOTAS              0x060006
 
+/* Orphan file without a journal */
+#define PR_6_ORPHAN_FILE_WITHOUT_JOURNAL       0x060007
+
+/* Orphan file truncation failed */
+#define PR_6_ORPHAN_FILE_TRUNC_FAILED  0x060008
+
+/* Failed to initialize orphan file */
+#define PR_6_ORPHAN_FILE_CORRUPTED     0x060009
+
+/* Cannot fix corrupted orphan file with invalid bitmaps */
+#define PR_6_ORPHAN_FILE_BITMAP_INVALID        0x06000A
+
+/* Orphan file creation failed */
+#define PR_6_ORPHAN_FILE_CREATE_FAILED 0x06000B
+
+/* Orphan file block contains data */
+#define PR_6_ORPHAN_BLOCK_DIRTY                0x06000C
+
+/* orphan_present set but orphan file is empty */
+#define PR_6_ORPHAN_PRESENT_CLEAN_FILE 0x06000D
+
+/* orphan_present set but orphan_file is not */
+#define PR_6_ORPHAN_PRESENT_NO_FILE    0x06000E
+
+/* Orphan file size isn't multiple of blocks size */
+#define PR_6_ORPHAN_FILE_WRONG_SIZE    0x06000F
+
+/* Orphan file contains holes */
+#define PR_6_ORPHAN_FILE_HOLE          0x060010
 
 /*
  * Function declarations
index 31e2ffb234d321961a03f0286079923472b37f3a..287b1a2be4ad74e2fc50f2a6c6744f4a10fac6c8 100644 (file)
@@ -313,6 +313,180 @@ static errcode_t e2fsck_write_all_quotas(e2fsck_t ctx)
        return pctx.errcode;
 }
 
+static int release_orphan_inode(e2fsck_t ctx, ext2_ino_t *ino, char *block_buf)
+{
+       ext2_filsys fs = ctx->fs;
+       struct problem_context pctx;
+       struct ext2_inode_large inode;
+       ext2_ino_t next_ino;
+
+       e2fsck_read_inode_full(ctx, *ino, EXT2_INODE(&inode),
+                               sizeof(inode), "release_orphan_inode");
+       clear_problem_context(&pctx);
+       pctx.ino = *ino;
+       pctx.inode = EXT2_INODE(&inode);
+       pctx.str = inode.i_links_count ? _("Truncating") : _("Clearing");
+
+       fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
+
+       next_ino = inode.i_dtime;
+       if (next_ino &&
+           ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
+            (next_ino > fs->super->s_inodes_count))) {
+               pctx.ino = next_ino;
+               fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
+               return 1;
+       }
+
+       if (release_inode_blocks(ctx, *ino, &inode, block_buf, &pctx))
+               return 1;
+
+       if (!inode.i_links_count) {
+               if (ctx->qctx)
+                       quota_data_inodes(ctx->qctx, &inode, *ino, -1);
+               ext2fs_inode_alloc_stats2(fs, *ino, -1,
+                                         LINUX_S_ISDIR(inode.i_mode));
+               ctx->free_inodes++;
+               inode.i_dtime = ctx->now;
+       } else {
+               inode.i_dtime = 0;
+       }
+       e2fsck_write_inode_full(ctx, *ino, EXT2_INODE(&inode),
+                               sizeof(inode), "delete_file");
+       *ino = next_ino;
+       return 0;
+}
+
+struct process_orphan_block_data {
+       e2fsck_t        ctx;
+       char            *buf;
+       char            *block_buf;
+       e2_blkcnt_t     blocks;
+       int             abort;
+       int             clear;
+       errcode_t       errcode;
+       ext2_ino_t      ino;
+       __u32           generation;
+};
+
+static int process_orphan_block(ext2_filsys fs,
+                              blk64_t  *block_nr,
+                              e2_blkcnt_t blockcnt,
+                              blk64_t  ref_blk EXT2FS_ATTR((unused)),
+                              int      ref_offset EXT2FS_ATTR((unused)),
+                              void *priv_data)
+{
+       struct process_orphan_block_data *pd;
+       e2fsck_t                ctx;
+       struct problem_context  pctx;
+       blk64_t                 blk = *block_nr;
+       struct ext4_orphan_block_tail *tail;
+       int                     j;
+       int                     inodes_per_ob;
+       __u32                   *bdata;
+       ext2_ino_t              ino;
+
+       pd = priv_data;
+       ctx = pd->ctx;
+       clear_problem_context(&pctx);
+       pctx.ino = fs->super->s_orphan_file_inum;
+       pctx.blk = blockcnt;
+
+       /* Orphan file must not have holes */
+       if (!blk) {
+               if (blockcnt == pd->blocks)
+                       return BLOCK_ABORT;
+               fix_problem(ctx, PR_0_ORPHAN_FILE_HOLE, &pctx);
+return_abort:
+               pd->abort = 1;
+               return BLOCK_ABORT;
+       }
+       inodes_per_ob = ext2fs_inodes_per_orphan_block(fs);
+       pd->errcode = io_channel_read_blk64(fs->io, blk, 1, pd->buf);
+       if (pd->errcode)
+               goto return_abort;
+       tail = ext2fs_orphan_block_tail(fs, pd->buf);
+       if (ext2fs_le32_to_cpu(tail->ob_magic) !=
+           EXT4_ORPHAN_BLOCK_MAGIC) {
+               fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_MAGIC, &pctx);
+               goto return_abort;
+       }
+       if (!ext2fs_orphan_file_block_csum_verify(fs,
+                       fs->super->s_orphan_file_inum, blk, pd->buf)) {
+               fix_problem(ctx, PR_0_ORPHAN_FILE_BAD_CHECKSUM, &pctx);
+               goto return_abort;
+       }
+       bdata = (__u32 *)pd->buf;
+       for (j = 0; j < inodes_per_ob; j++) {
+               if (!bdata[j])
+                       continue;
+               ino = ext2fs_le32_to_cpu(bdata[j]);
+               if (release_orphan_inode(ctx, &ino, pd->block_buf))
+                       goto return_abort;
+       }
+       return 0;
+}
+
+static int process_orphan_file(e2fsck_t ctx, char *block_buf)
+{
+       ext2_filsys fs = ctx->fs;
+       char *orphan_buf;
+       struct process_orphan_block_data pd;
+       int ret = 0;
+       ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum;
+       struct ext2_inode orphan_inode;
+       struct problem_context  pctx;
+       errcode_t retval;
+
+       if (!ext2fs_has_feature_orphan_file(fs->super))
+               return 0;
+
+       clear_problem_context(&pctx);
+       pctx.ino = orphan_inum;
+
+       orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
+                                                   "orphan block buffer");
+       retval = ext2fs_read_inode(fs, orphan_inum, &orphan_inode);
+       if (retval < 0) {
+               com_err("process_orphan_file", retval,
+                       _("while reading inode %d"), orphan_inum);
+               ret = 1;
+               goto out;
+       }
+       if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) {
+               fix_problem(ctx, PR_0_ORPHAN_FILE_WRONG_SIZE, &pctx);
+               ret = 1;
+               goto out;
+       }
+       pd.buf = orphan_buf + 3 * fs->blocksize;
+       pd.block_buf = block_buf;
+       pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize;
+       pd.ctx = ctx;
+       pd.abort = 0;
+       pd.errcode = 0;
+       retval = ext2fs_block_iterate3(fs, orphan_inum,
+                                      BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE,
+                                      orphan_buf, process_orphan_block, &pd);
+       if (retval) {
+               com_err("process_orphan_block", retval,
+                       _("while calling ext2fs_block_iterate for inode %d"),
+                       orphan_inum);
+               ret = 1;
+               goto out;
+       }
+       if (pd.abort) {
+               if (pd.errcode) {
+                       com_err("process_orphan_block", pd.errcode,
+                               _("while reading blocks of inode %d"),
+                               orphan_inum);
+               }
+               ret = 1;
+       }
+out:
+       ext2fs_free_mem(&orphan_buf);
+       return ret;
+}
+
 /*
  * This function releases all of the orphan inodes.  It returns 1 if
  * it hit some error, and 0 on success.
@@ -325,10 +499,13 @@ static int release_orphan_inodes(e2fsck_t ctx)
        struct problem_context pctx;
        char *block_buf;
 
-       if ((ino = fs->super->s_last_orphan) == 0)
+       if (fs->super->s_last_orphan == 0 &&
+           !ext2fs_has_feature_orphan_present(fs->super))
                return 0;
 
        clear_problem_context(&pctx);
+       ino = fs->super->s_last_orphan;
+       pctx.ino = ino;
        pctx.errcode = e2fsck_read_all_quotas(ctx);
        if (pctx.errcode) {
                fix_problem(ctx, PR_0_QUOTA_INIT_CTX, &pctx);
@@ -343,9 +520,10 @@ static int release_orphan_inodes(e2fsck_t ctx)
        ext2fs_mark_super_dirty(fs);
 
        /*
-        * If the filesystem contains errors, don't run the orphan
-        * list, since the orphan list can't be trusted; and we're
-        * going to be running a full e2fsck run anyway...
+        * If the filesystem contains errors, don't process the orphan list
+        * or orphan file, since neither can be trusted; and we're going to
+        * be running a full e2fsck run anyway... We clear orphan file contents
+        * after filesystem is checked to avoid clearing someone else's data.
         */
        if (fs->super->s_state & EXT2_ERROR_FS) {
                if (ctx->qctx)
@@ -353,10 +531,8 @@ static int release_orphan_inodes(e2fsck_t ctx)
                return 0;
        }
 
-       if ((ino < EXT2_FIRST_INODE(fs->super)) ||
-           (ino > fs->super->s_inodes_count)) {
-               clear_problem_context(&pctx);
-               pctx.ino = ino;
+       if (ino && ((ino < EXT2_FIRST_INODE(fs->super)) ||
+           (ino > fs->super->s_inodes_count))) {
                fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
                goto err_qctx;
        }
@@ -365,43 +541,19 @@ static int release_orphan_inodes(e2fsck_t ctx)
                                                    "block iterate buffer");
        e2fsck_read_bitmaps(ctx);
 
+       /* First process orphan list */
        while (ino) {
-               e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
-                               sizeof(inode), "release_orphan_inodes");
-               clear_problem_context(&pctx);
-               pctx.ino = ino;
-               pctx.inode = EXT2_INODE(&inode);
-               pctx.str = inode.i_links_count ? _("Truncating") :
-                       _("Clearing");
-
-               fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
-
-               next_ino = inode.i_dtime;
-               if (next_ino &&
-                   ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
-                    (next_ino > fs->super->s_inodes_count))) {
-                       pctx.ino = next_ino;
-                       fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
+               if (release_orphan_inode(ctx, &ino, block_buf))
                        goto err_buf;
-               }
+       }
 
-               if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
-                       goto err_buf;
+       /* Next process orphan file */
+       if (ext2fs_has_feature_orphan_present(fs->super) &&
+           !ext2fs_has_feature_orphan_file(fs->super))
+               goto err_buf;
+       if (process_orphan_file(ctx, block_buf))
+               goto err_buf;
 
-               if (!inode.i_links_count) {
-                       if (ctx->qctx)
-                               quota_data_inodes(ctx->qctx, &inode, ino, -1);
-                       ext2fs_inode_alloc_stats2(fs, ino, -1,
-                                                 LINUX_S_ISDIR(inode.i_mode));
-                       ctx->free_inodes++;
-                       inode.i_dtime = ctx->now;
-               } else {
-                       inode.i_dtime = 0;
-               }
-               e2fsck_write_inode_full(ctx, ino, EXT2_INODE(&inode),
-                               sizeof(inode), "delete_file");
-               ino = next_ino;
-       }
        ext2fs_free_mem(&block_buf);
        pctx.errcode = e2fsck_write_all_quotas(ctx);
        if (pctx.errcode)
@@ -416,6 +568,130 @@ err:
        return 1;
 }
 
+static int reinit_orphan_block(ext2_filsys fs,
+                              blk64_t  *block_nr,
+                              e2_blkcnt_t blockcnt,
+                              blk64_t  ref_blk EXT2FS_ATTR((unused)),
+                              int      ref_offset EXT2FS_ATTR((unused)),
+                              void *priv_data)
+{
+       struct process_orphan_block_data *pd;
+       e2fsck_t                ctx;
+       blk64_t                 blk = *block_nr;
+       struct problem_context  pctx;
+       struct ext4_orphan_block_tail *tail;
+
+       pd = priv_data;
+       ctx = pd->ctx;
+       tail = ext2fs_orphan_block_tail(fs, pd->buf);
+
+       /* Orphan file must not have holes */
+       if (!blk) {
+               if (blockcnt == pd->blocks)
+                       return BLOCK_ABORT;
+
+               clear_problem_context(&pctx);
+               pctx.ino = fs->super->s_orphan_file_inum;
+               pctx.blk = blockcnt;
+               fix_problem(ctx, PR_6_ORPHAN_FILE_HOLE, &pctx);
+return_abort:
+               pd->abort = 1;
+               return BLOCK_ABORT;
+       }
+       /*
+        * Update checksum to match expected buffer contents with appropriate
+        * block number.
+        */
+       tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs, pd->ino,
+                                               pd->generation, blk, pd->buf);
+       if (!pd->clear) {
+               pd->errcode = io_channel_read_blk64(fs->io, blk, 1,
+                                                   pd->block_buf);
+               /* Block is already cleanly initialized? */
+               if (!memcmp(pd->block_buf, pd->buf, fs->blocksize))
+                       return 0;
+
+               clear_problem_context(&pctx);
+               pctx.ino = fs->super->s_orphan_file_inum;
+               pctx.blk = blockcnt;
+               if (!fix_problem(ctx, PR_6_ORPHAN_BLOCK_DIRTY, &pctx))
+                       goto return_abort;
+               pd->clear = 1;
+       }
+       pd->errcode = io_channel_write_blk64(fs->io, blk, 1, pd->buf);
+       if (pd->errcode)
+               goto return_abort;
+       return 0;
+}
+
+/*
+ * Check and clear orphan file. We just return non-zero if we hit some
+ * inconsistency. Caller will truncate & recreate new orphan file.
+ */
+int check_init_orphan_file(e2fsck_t ctx)
+{
+       ext2_filsys fs = ctx->fs;
+       char *orphan_buf;
+       struct process_orphan_block_data pd;
+       struct ext4_orphan_block_tail *tail;
+       ext2_ino_t orphan_inum = fs->super->s_orphan_file_inum;
+       struct ext2_inode orphan_inode;
+       int ret = 0;
+       errcode_t retval;
+
+       orphan_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 5,
+                                                   "orphan block buffer");
+       e2fsck_read_inode(ctx, orphan_inum, &orphan_inode, "orphan inode");
+       if (EXT2_I_SIZE(&orphan_inode) & (fs->blocksize - 1)) {
+               struct problem_context  pctx;
+
+               clear_problem_context(&pctx);
+               pctx.ino = orphan_inum;
+               fix_problem(ctx, PR_6_ORPHAN_FILE_WRONG_SIZE, &pctx);
+               ret = 1;
+               goto out;
+       }
+       pd.buf = orphan_buf + 3 * fs->blocksize;
+       pd.block_buf = orphan_buf + 4 * fs->blocksize;
+       pd.blocks = EXT2_I_SIZE(&orphan_inode) / fs->blocksize;
+       pd.ctx = ctx;
+       pd.abort = 0;
+       pd.clear = 0;
+       pd.errcode = 0;
+       pd.ino = orphan_inum;
+       pd.generation = orphan_inode.i_generation;
+       /* Initialize buffer to write */
+       memset(pd.buf, 0, fs->blocksize);
+       tail = ext2fs_orphan_block_tail(fs, pd.buf);
+       tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC);
+
+       retval = ext2fs_block_iterate3(fs, orphan_inum,
+                                      BLOCK_FLAG_DATA_ONLY | BLOCK_FLAG_HOLE,
+                                      orphan_buf, reinit_orphan_block, &pd);
+       if (retval) {
+               com_err("reinit_orphan_block", retval,
+                       _("while calling ext2fs_block_iterate for inode %d"),
+                       orphan_inum);
+               ret = 1;
+               goto out;
+       }
+       if (pd.abort) {
+               if (pd.errcode) {
+                       com_err("process_orphan_block", pd.errcode,
+                               _("while reading blocks of inode %d"),
+                               orphan_inum);
+               }
+               ret = 1;
+       }
+
+       /* We had to clear some blocks. Report it up. */
+       if (ret == 0 && pd.clear)
+               ret = 2;
+out:
+       ext2fs_free_mem(&orphan_buf);
+       return ret;
+}
+
 /*
  * Check the resize inode to make sure it is sane.  We check both for
  * the case where on-line resizing is not enabled (in which case the
index c5f9e4415f8fe8241a1e77250bc2f1df063e39fe..bf9b0bf2ecb8f480135ef9abb55b8c02fe6155fe 100644 (file)
@@ -1945,15 +1945,82 @@ print_unsupp_features:
                                _("\n*** journal has been regenerated ***\n"));
                }
        }
-no_journal:
 
+no_journal:
        if (run_result & E2F_FLAG_ABORT) {
                fatal_error(ctx, _("aborted"));
        } else if (run_result & E2F_FLAG_CANCEL) {
                log_out(ctx, _("%s: e2fsck canceled.\n"), ctx->device_name ?
                        ctx->device_name : ctx->filesystem_name);
                exit_value |= FSCK_CANCELED;
-       } else if (ctx->qctx && !ctx->invalid_bitmaps) {
+               goto cleanup;
+       }
+
+       if (ext2fs_has_feature_orphan_file(fs->super)) {
+               int ret;
+
+               /* No point in orphan file without a journal... */
+               if (!ext2fs_has_feature_journal(fs->super) &&
+                   fix_problem(ctx, PR_6_ORPHAN_FILE_WITHOUT_JOURNAL, &pctx)) {
+                       retval = ext2fs_truncate_orphan_file(fs);
+                       if (retval) {
+                               /* Huh, failed to delete file */
+                               fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED,
+                                           &pctx);
+                               goto check_quotas;
+                       }
+                       ext2fs_clear_feature_orphan_file(fs->super);
+                       ext2fs_mark_super_dirty(fs);
+                       goto check_quotas;
+               }
+               ret = check_init_orphan_file(ctx);
+               if (ret == 2 ||
+                   (ret == 0 && ext2fs_has_feature_orphan_present(fs->super) &&
+                    fix_problem(ctx, PR_6_ORPHAN_PRESENT_CLEAN_FILE, &pctx))) {
+                       ext2fs_clear_feature_orphan_present(fs->super);
+                       ext2fs_mark_super_dirty(fs);
+               } else if (ret == 1 &&
+                   fix_problem(ctx, PR_6_ORPHAN_FILE_CORRUPTED, &pctx)) {
+                       int orphan_file_blocks;
+
+                       if (ctx->invalid_bitmaps) {
+                               fix_problem(ctx,
+                                           PR_6_ORPHAN_FILE_BITMAP_INVALID,
+                                           &pctx);
+                               goto check_quotas;
+                       }
+
+                       retval = ext2fs_truncate_orphan_file(fs);
+                       if (retval) {
+                               /* Huh, failed to truncate file */
+                               fix_problem(ctx, PR_6_ORPHAN_FILE_TRUNC_FAILED,
+                                           &pctx);
+                               goto check_quotas;
+                       }
+
+                       orphan_file_blocks =
+                               ext2fs_default_orphan_file_blocks(fs);
+                       log_out(ctx, _("Creating orphan file (%d blocks): "),
+                               orphan_file_blocks);
+                       fflush(stdout);
+                       retval = ext2fs_create_orphan_file(fs,
+                                                          orphan_file_blocks);
+                       if (retval) {
+                               log_out(ctx, "%s: while trying to create "
+                                       "orphan file\n", error_message(retval));
+                               fix_problem(ctx, PR_6_ORPHAN_FILE_CREATE_FAILED,
+                                           &pctx);
+                               goto check_quotas;
+                       }
+                       log_out(ctx, "%s", _(" Done.\n"));
+               }
+       } else if (ext2fs_has_feature_orphan_present(fs->super) &&
+                  fix_problem(ctx, PR_6_ORPHAN_PRESENT_NO_FILE, &pctx)) {
+                       ext2fs_clear_feature_orphan_present(fs->super);
+                       ext2fs_mark_super_dirty(fs);
+       }
+check_quotas:
+       if (ctx->qctx && !ctx->invalid_bitmaps) {
                int needs_writeout;
 
                for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
@@ -1988,6 +2055,7 @@ no_journal:
                goto restart;
        }
 
+cleanup:
 #ifdef MTRACE
        mtrace_print("Cleanup");
 #endif