Whamcloud - gitweb
Merge branch 'maint'
[tools/e2fsprogs.git] / e2fsck / rehash.c
index 1f0a0b8..5592e3f 100644 (file)
 #include "e2fsck.h"
 #include "problem.h"
 
+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+       if (!ctx->dirs_to_hash)
+               ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+       if (ctx->dirs_to_hash)
+               ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+       if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+               return 1;
+       if (!ctx->dirs_to_hash)
+               return 0;
+       return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
 struct fill_dir_struct {
        char *buf;
        struct ext2_inode *inode;
@@ -59,9 +78,10 @@ struct fill_dir_struct {
        e2fsck_t ctx;
        struct hash_entry *harray;
        int max_array, num_array;
-       int dir_size;
+       unsigned int dir_size;
        int compress;
        ino_t parent;
+       ext2_ino_t dir;
 };
 
 struct hash_entry {
@@ -106,7 +126,10 @@ static int fill_dir_block(ext2_filsys fs,
                dirent = (struct ext2_dir_entry *) dir;
                (void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
        } else {
-               fd->err = ext2fs_read_dir_block3(fs, *block_nr, dir, 0);
+               fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
+               fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
+                                                fd->dir);
+               fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
                if (fd->err)
                        return BLOCK_ABORT;
        }
@@ -122,7 +145,7 @@ static int fill_dir_block(ext2_filsys fs,
                if (((dir_offset + rec_len) > fs->blocksize) ||
                    (rec_len < 8) ||
                    ((rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > rec_len)) {
+                   (((dirent->name_len & 0xFF)+8U) > rec_len)) {
                        fd->err = EXT2_ET_DIR_CORRUPTED;
                        return BLOCK_ABORT;
                }
@@ -404,10 +427,11 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        char                    *block_start;
        struct hash_entry       *ent;
        struct ext2_dir_entry   *dirent;
-       unsigned int            rec_len, prev_rec_len;
-       int                     i, left;
+       unsigned int            rec_len, prev_rec_len, left, slack, offset;
+       int                     i;
        ext2_dirhash_t          prev_hash;
-       int                     offset, slack;
+       int                     csum_size = 0;
+       struct                  ext2_dir_entry_tail *t;
 
        if (ctx->htree_slack_percentage == 255) {
                profile_get_uint(ctx->profile, "options",
@@ -418,6 +442,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                        ctx->htree_slack_percentage = 20;
        }
 
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dir_entry_tail);
+
        outdir->max = 0;
        retval = alloc_size_dir(fs, outdir,
                                (fd->dir_size / fs->blocksize) + 2);
@@ -432,9 +460,9 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        dirent = (struct ext2_dir_entry *) block_start;
        prev_rec_len = 0;
        rec_len = 0;
-       left = fs->blocksize;
+       left = fs->blocksize - csum_size;
        slack = fd->compress ? 12 :
-               (fs->blocksize * ctx->htree_slack_percentage)/100;
+               ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
        if (slack < 12)
                slack = 12;
        for (i = 0; i < fd->num_array; i++) {
@@ -449,12 +477,17 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                                if (retval)
                                        return retval;
                        }
+                       if (csum_size) {
+                               t = EXT2_DIRENT_TAIL(block_start,
+                                                    fs->blocksize);
+                               ext2fs_initialize_dirent_tail(fs, t);
+                       }
                        if ((retval = get_next_block(fs, outdir,
                                                      &block_start)))
                                return retval;
                        offset = 0;
                }
-               left = fs->blocksize - offset;
+               left = (fs->blocksize - csum_size) - offset;
                dirent = (struct ext2_dir_entry *) (block_start + offset);
                if (offset == 0) {
                        if (ent->hash == prev_hash)
@@ -483,6 +516,10 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        }
        if (left)
                retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
+       if (csum_size) {
+               t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
+               ext2fs_initialize_dirent_tail(fs, t);
+       }
 
        return retval;
 }
@@ -495,6 +532,7 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
        struct ext2_dx_root_info        *root;
        struct ext2_dx_countlimit       *limits;
        int                             filetype = 0;
+       int                             csum_size = 0;
 
        if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
                filetype = EXT2_FT_DIR << 8;
@@ -519,8 +557,13 @@ static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
        root->indirect_levels = 0;
        root->unused_flags = 0;
 
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dx_tail);
+
        limits = (struct ext2_dx_countlimit *) (buf+32);
-       limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+       limits->limit = (fs->blocksize - (32 + csum_size)) /
+                       sizeof(struct ext2_dx_entry);
        limits->count = 0;
 
        return root;
@@ -531,14 +574,20 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
 {
        struct ext2_dir_entry           *dir;
        struct ext2_dx_countlimit       *limits;
+       int                             csum_size = 0;
 
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
        dir->inode = 0;
        (void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
 
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dx_tail);
+
        limits = (struct ext2_dx_countlimit *) (buf+8);
-       limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+       limits->limit = (fs->blocksize - (8 + csum_size)) /
+                       sizeof(struct ext2_dx_entry);
        limits->count = 0;
 
        return (struct ext2_dx_entry *) limits;
@@ -628,6 +677,7 @@ struct write_dir_struct {
        errcode_t       err;
        e2fsck_t        ctx;
        int             cleared;
+       ext2_ino_t      dir;
 };
 
 /*
@@ -642,11 +692,23 @@ static int write_dir_block(ext2_filsys fs,
 {
        struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
        blk64_t blk;
-       char    *dir;
+       char    *dir, *buf = 0;
 
        if (*block_nr == 0)
                return 0;
-       if (blockcnt >= wd->outdir->num) {
+       if (blockcnt < 0)
+               return 0;
+       if (blockcnt < wd->outdir->num)
+               dir = wd->outdir->buf + (blockcnt * fs->blocksize);
+       else if (wd->ctx->lost_and_found == wd->dir) {
+               /* Don't release any extra directory blocks for lost+found */
+               wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf);
+               if (wd->err)
+                       return BLOCK_ABORT;
+               dir = buf;
+               wd->outdir->num++;
+       } else {
+               /* We don't need this block, so release it */
                e2fsck_read_bitmaps(wd->ctx);
                blk = *block_nr;
                ext2fs_unmark_block_bitmap2(wd->ctx->block_found_map, blk);
@@ -655,11 +717,11 @@ static int write_dir_block(ext2_filsys fs,
                wd->cleared++;
                return BLOCK_CHANGED;
        }
-       if (blockcnt < 0)
-               return 0;
 
-       dir = wd->outdir->buf + (blockcnt * fs->blocksize);
-       wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0);
+       wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
+       if (buf)
+               ext2fs_free_mem(&buf);
+
        if (wd->err)
                return BLOCK_ABORT;
        return 0;
@@ -681,6 +743,7 @@ static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
        wd.err = 0;
        wd.ctx = ctx;
        wd.cleared = 0;
+       wd.dir = ino;
 
        retval = ext2fs_block_iterate3(fs, ino, 0, 0,
                                       write_dir_block, &wd);
@@ -733,6 +796,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
        fd.err = 0;
        fd.dir_size = 0;
        fd.compress = 0;
+       fd.dir = ino;
        if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
            (inode.i_size / fs->blocksize) < 2)
                fd.compress = 1;
@@ -836,7 +900,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
        if (!ctx->dirs_to_hash && !all_dirs)
                return;
 
-       e2fsck_get_lost_and_found(ctx, 0);
+       (void) e2fsck_get_lost_and_found(ctx, 0);
 
        clear_problem_context(&pctx);
 
@@ -864,8 +928,7 @@ void e2fsck_rehash_directories(e2fsck_t ctx)
                        if (!ext2fs_u32_list_iterate(iter, &ino))
                                break;
                }
-               if (ino == ctx->lost_and_found)
-                       continue;
+
                pctx.dir = ino;
                if (first) {
                        fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);