]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
libext2fs: add checksums to the end of directory leaf nodes
authorDarrick J. Wong <djwong@us.ibm.com>
Thu, 2 Aug 2012 21:27:43 +0000 (17:27 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 2 Aug 2012 21:27:43 +0000 (17:27 -0400)
Introduce small structures for recording directory tree checksums, and
some API changes to support writing out directory blocks with
checksums.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
15 files changed:
debugfs/htree.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/pass3.c
e2fsck/rehash.c
lib/ext2fs/csum.c
lib/ext2fs/dir_iterate.c
lib/ext2fs/dirblock.c
lib/ext2fs/expanddir.c
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2fs.h
lib/ext2fs/link.c
lib/ext2fs/mkdir.c
lib/ext2fs/newdir.c
lib/ext2fs/swapfs.c

index 2fb804edbb7938376309fb14c98ff4caf5302a10..1932962d4fb3230271d2529886ed935f5b0a78b4 100644 (file)
@@ -44,6 +44,11 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
        ext2_dirhash_t  hash, minor_hash;
        unsigned int    rec_len;
        int             hash_alg;
+       int             csum_size = 0;
+
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dir_entry_tail);
 
        errcode = ext2fs_bmap2(fs, ino, inode, buf, 0, blk, 0, &pblk);
        if (errcode) {
@@ -53,7 +58,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
        }
 
        printf("Reading directory block %llu, phys %llu\n", blk, pblk);
-       errcode = ext2fs_read_dir_block2(current_fs, pblk, buf, 0);
+       errcode = ext2fs_read_dir_block4(current_fs, pblk, buf, 0, ino);
        if (errcode) {
                com_err("htree_dump_leaf_node", errcode,
                        "while reading block %llu (%llu)\n",
@@ -65,7 +70,7 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
            (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
                hash_alg += 3;
 
-       while (offset < fs->blocksize) {
+       while (offset < (fs->blocksize - csum_size)) {
                dirent = (struct ext2_dir_entry *) (buf + offset);
                errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
                if (errcode) {
index e1d6e91d5db25ceade8b04bcc8b70ba2c9140f2a..2d39d038a5c1dd75602b810b669db8da22254143 100644 (file)
@@ -474,7 +474,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
 
        /* read the first block */
        ehandler_operation(_("reading directory block"));
-       retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0);
+       retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino);
        ehandler_operation(0);
        if (retval)
                return;
index e4a06f3e93f6e0880a16e8e9b25f08cc522c89b4..5de301f1a07b5d1bf4d46a0963281b9d4b1e1405 100644 (file)
@@ -754,6 +754,7 @@ static int check_dir_block(ext2_filsys fs,
        int     dups_found = 0;
        int     ret;
        int     dx_csum_size = 0;
+       int     failed_csum = 0;
 
        cd = (struct check_dir_struct *) priv_data;
        buf = cd->buf;
@@ -804,10 +805,14 @@ static int check_dir_block(ext2_filsys fs,
 #endif
 
        ehandler_operation(_("reading directory block"));
-       cd->pctx.errcode = ext2fs_read_dir_block3(fs, block_nr, buf, 0);
+       cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
        ehandler_operation(0);
        if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
                cd->pctx.errcode = 0; /* We'll handle this ourselves */
+       else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
+               cd->pctx.errcode = 0; /* We'll handle this ourselves */
+               failed_csum = 1;
+       }
        if (cd->pctx.errcode) {
                if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
                        ctx->flags |= E2F_FLAG_ABORT;
@@ -1145,7 +1150,7 @@ out_htree:
                cd->pctx.dir = cd->pctx.ino;
                if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
                    (dx_db->type == DX_DIRBLOCK_NODE))
-                       parse_int_node(fs, db, cd, dx_dir, buf, 0);
+                       parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
        }
 #endif /* ENABLE_HTREE */
        if (offset != fs->blocksize) {
@@ -1156,7 +1161,8 @@ out_htree:
                }
        }
        if (dir_modified) {
-               cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+               cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
+                                                          0, ino);
                if (cd->pctx.errcode) {
                        if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
                                         &cd->pctx))
@@ -1476,7 +1482,7 @@ static int allocate_dir_block(e2fsck_t ctx,
                return 1;
        }
 
-       pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
+       pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino);
        ext2fs_free_mem(&block);
        if (pctx->errcode) {
                pctx->str = "ext2fs_write_dir_block";
index 565b8e33fe0924fd464bb06dbf8aa3624b2a7b93..4ae3a338357b3228e6f94b2610f39aa16e541b2e 100644 (file)
@@ -198,7 +198,8 @@ static void check_root(e2fsck_t ctx)
                return;
        }
 
-       pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+       pctx.errcode = ext2fs_write_dir_block4(fs, blk, block, 0,
+                                              EXT2_ROOT_INO);
        if (pctx.errcode) {
                pctx.str = "ext2fs_write_dir_block";
                fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
@@ -444,7 +445,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
                return 0;
        }
 
-       retval = ext2fs_write_dir_block(fs, blk, block);
+       retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
        ext2fs_free_mem(&block);
        if (retval) {
                pctx.errcode = retval;
@@ -685,6 +686,7 @@ struct expand_dir_struct {
        blk64_t                 last_block;
        errcode_t               err;
        e2fsck_t                ctx;
+       ext2_ino_t              dir;
 };
 
 static int expand_dir_proc(ext2_filsys fs,
@@ -725,7 +727,8 @@ static int expand_dir_proc(ext2_filsys fs,
                        return BLOCK_ABORT;
                }
                es->num--;
-               retval = ext2fs_write_dir_block(fs, new_blk, block);
+               retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+                                                es->dir);
        } else {
                retval = ext2fs_get_mem(fs->blocksize, &block);
                if (retval) {
@@ -778,6 +781,7 @@ errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
        es.err = 0;
        es.newblocks = 0;
        es.ctx = ctx;
+       es.dir = dir;
 
        retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
                                       0, expand_dir_proc, &es);
index ed719ce2cb249c92793d608f83b0db17375d23fc..b159bb7b292d1f437971d43209c4841c52733dd4 100644 (file)
@@ -81,6 +81,7 @@ struct fill_dir_struct {
        int dir_size;
        int compress;
        ino_t parent;
+       ext2_ino_t dir;
 };
 
 struct hash_entry {
@@ -125,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;
        }
@@ -416,7 +420,8 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
 
 static errcode_t copy_dir_entries(e2fsck_t ctx,
                                  struct fill_dir_struct *fd,
-                                 struct out_dir *outdir)
+                                 struct out_dir *outdir,
+                                 ext2_ino_t ino)
 {
        ext2_filsys             fs = ctx->fs;
        errcode_t               retval;
@@ -427,6 +432,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        int                     i, left;
        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",
@@ -437,6 +444,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);
@@ -451,9 +462,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++) {
@@ -468,12 +479,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)
@@ -502,6 +518,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;
 }
@@ -659,6 +679,7 @@ struct write_dir_struct {
        errcode_t       err;
        e2fsck_t        ctx;
        int             cleared;
+       ext2_ino_t      dir;
 };
 
 /*
@@ -690,7 +711,7 @@ static int write_dir_block(ext2_filsys fs,
                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 (wd->err)
                return BLOCK_ABORT;
        return 0;
@@ -712,6 +733,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);
@@ -764,6 +786,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;
@@ -823,7 +846,7 @@ resort:
         * Copy the directory entries.  In a htree directory these
         * will become the leaf nodes.
         */
-       retval = copy_dir_entries(ctx, &fd, &outdir);
+       retval = copy_dir_entries(ctx, &fd, &outdir, ino);
        if (retval)
                goto errout;
 
index 262eb75973c8b34d5c758a727aeb5d06365d61af..e92bac536549748ed3244d5a27f2b0ad0783eed3 100644 (file)
@@ -93,6 +93,122 @@ errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
        return __get_dx_countlimit(fs, dirent, cc, offset, 0);
 }
 
+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+                                  struct ext2_dir_entry_tail *t)
+{
+       memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+       ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+                          (struct ext2_dir_entry *)t);
+       t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static errcode_t __get_dirent_tail(ext2_filsys fs,
+                                  struct ext2_dir_entry *dirent,
+                                  struct ext2_dir_entry_tail **tt,
+                                  int need_swab)
+{
+       struct ext2_dir_entry *d;
+       void *top;
+       struct ext2_dir_entry_tail *t;
+       unsigned int rec_len;
+       errcode_t retval = 0;
+       __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+       d = dirent;
+       top = EXT2_DIRENT_TAIL(dirent, fs->blocksize);
+
+       rec_len = translate(d->rec_len);
+       while (rec_len && !(rec_len & 0x3)) {
+               d = (struct ext2_dir_entry *)(((void *)d) + rec_len);
+               if ((void *)d >= top)
+                       break;
+               rec_len = translate(d->rec_len);
+       }
+
+       if (d != top)
+               return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+       t = (struct ext2_dir_entry_tail *)d;
+       if (t->det_reserved_zero1 ||
+           translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+           translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM)
+               return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+       if (tt)
+               *tt = t;
+       return retval;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+       return __get_dirent_tail(fs, dirent, NULL, 0) == 0;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+                                   struct ext2_dir_entry *dirent, __u32 *crc,
+                                   int size)
+{
+       errcode_t retval;
+       char *buf = (char *)dirent;
+       __u32 gen;
+       struct ext2_inode inode;
+
+       retval = ext2fs_read_inode(fs, inum, &inode);
+       if (retval)
+               return retval;
+
+       inum = ext2fs_cpu_to_le32(inum);
+       gen = ext2fs_cpu_to_le32(inode.i_generation);
+       *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+                               sizeof(inum));
+       *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+       *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+
+       return 0;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+                             struct ext2_dir_entry *dirent)
+{
+       errcode_t retval;
+       __u32 calculated;
+       struct ext2_dir_entry_tail *t;
+
+       retval = __get_dirent_tail(fs, dirent, &t, 1);
+       if (retval)
+               return 1;
+
+       /*
+        * The checksum field is overlaid with the dirent->name field
+        * so the swapfs.c functions won't change the endianness.
+        */
+       retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+                                   (void *)t - (void *)dirent);
+       if (retval)
+               return 0;
+       return ext2fs_le32_to_cpu(t->det_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+                                       struct ext2_dir_entry *dirent)
+{
+       errcode_t retval;
+       __u32 crc;
+       struct ext2_dir_entry_tail *t;
+
+       retval = __get_dirent_tail(fs, dirent, &t, 1);
+       if (retval)
+               return retval;
+
+       /* swapfs.c functions don't change the checksum endianness */
+       retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+                                   (void *)t - (void *)dirent);
+       if (retval)
+               return retval;
+       t->det_checksum = ext2fs_cpu_to_le32(crc);
+       return 0;
+}
+
 static errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
                                struct ext2_dir_entry *dirent,
                                __u32 *crc, int count_offset, int count,
@@ -179,6 +295,38 @@ static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
        return retval;
 }
 
+int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+                                struct ext2_dir_entry *dirent)
+{
+       if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 1;
+
+       if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+               return ext2fs_dirent_csum_verify(fs, inum, dirent);
+       if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+               return ext2fs_dx_csum_verify(fs, inum, dirent);
+
+       return 0;
+}
+
+errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+                                   struct ext2_dir_entry *dirent)
+{
+       if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               return 0;
+
+       if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+               return ext2fs_dirent_csum_set(fs, inum, dirent);
+       if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+               return ext2fs_dx_csum_set(fs, inum, dirent);
+
+       if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+               return 0;
+       return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
 #define EXT3_EXTENT_TAIL_OFFSET(hdr)   (sizeof(struct ext3_extent_header) + \
        (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
 
index 5125d19919bb5904cabf2be5aff51771f42e4568..c24015cc0519ca0d1097210c435d2daeb07e58ff 100644 (file)
@@ -192,17 +192,23 @@ int ext2fs_process_dir_block(ext2_filsys fs,
        unsigned int    rec_len, size;
        int             entry;
        struct ext2_dir_entry *dirent;
+       int             csum_size = 0;
 
        if (blockcnt < 0)
                return 0;
 
        entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
 
-       ctx->errcode = ext2fs_read_dir_block3(fs, *blocknr, ctx->buf, 0);
+       ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+                                             ctx->dir);
        if (ctx->errcode)
                return BLOCK_ABORT;
 
-       while (offset < fs->blocksize) {
+       if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dir_entry_tail);
+
+       while (offset < (fs->blocksize - csum_size)) {
                dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
                if (ext2fs_get_rec_len(fs, dirent, &rec_len))
                        return BLOCK_ABORT;
@@ -259,8 +265,8 @@ next:
        }
 
        if (changed) {
-               ctx->errcode = ext2fs_write_dir_block3(fs, *blocknr, ctx->buf,
-                                                      0);
+               ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
+                                                      0, ctx->dir);
                if (ctx->errcode)
                        return BLOCK_ABORT;
        }
index cb3a104c781854fe3575484a98d338a8e89016b4..54b2777285150db4fcf198ff5aa7875484b1841a 100644 (file)
 #include "ext2_fs.h"
 #include "ext2fs.h"
 
-errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
-                                void *buf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+                                void *buf, int flags EXT2FS_ATTR((unused)),
+                                ext2_ino_t ino)
 {
        errcode_t       retval;
-       char            *p, *end;
-       struct ext2_dir_entry *dirent;
-       unsigned int    name_len, rec_len;
-
+       int             corrupt = 0;
 
        retval = io_channel_read_blk64(fs->io, block, 1, buf);
        if (retval)
                return retval;
 
-       p = (char *) buf;
-       end = (char *) buf + fs->blocksize;
-       while (p < end-8) {
-               dirent = (struct ext2_dir_entry *) p;
-#ifdef WORDS_BIGENDIAN
-               dirent->inode = ext2fs_swab32(dirent->inode);
-               dirent->rec_len = ext2fs_swab16(dirent->rec_len);
-               dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
-               name_len = dirent->name_len;
+       if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+           !ext2fs_dir_block_csum_verify(fs, ino,
+                                         (struct ext2_dir_entry *)buf))
+               corrupt = 1;
+
 #ifdef WORDS_BIGENDIAN
-               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
-                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+       retval = ext2fs_dirent_swab_in(fs, buf, flags);
 #endif
-               if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
-                       return retval;
-               if ((rec_len < 8) || (rec_len % 4)) {
-                       rec_len = 8;
-                       retval = EXT2_ET_DIR_CORRUPTED;
-               } else if (((name_len & 0xFF) + 8) > rec_len)
-                       retval = EXT2_ET_DIR_CORRUPTED;
-               p += rec_len;
-       }
+       if (!retval && corrupt)
+               retval = EXT2_ET_DIR_CSUM_INVALID;
        return retval;
 }
 
+errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
+                                void *buf, int flags EXT2FS_ATTR((unused)))
+{
+       return ext2fs_read_dir_block4(fs, block, buf, flags, 0);
+}
+
 errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
                                 void *buf, int flags EXT2FS_ATTR((unused)))
 {
@@ -72,45 +63,40 @@ errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
 }
 
 
-errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
-                                 void *inbuf, int flags EXT2FS_ATTR((unused)))
+errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+                                 void *inbuf, int flags EXT2FS_ATTR((unused)),
+                                 ext2_ino_t ino)
 {
-#ifdef WORDS_BIGENDIAN
        errcode_t       retval;
-       char            *p, *end;
-       char            *buf = 0;
-       unsigned int    rec_len;
-       struct ext2_dir_entry *dirent;
+       char            *buf = inbuf;
 
+#ifdef WORDS_BIGENDIAN
        retval = ext2fs_get_mem(fs->blocksize, &buf);
        if (retval)
                return retval;
        memcpy(buf, inbuf, fs->blocksize);
-       p = buf;
-       end = buf + fs->blocksize;
-       while (p < end) {
-               dirent = (struct ext2_dir_entry *) p;
-               if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
-                       return retval;
-               if ((rec_len < 8) ||
-                   (rec_len % 4)) {
-                       ext2fs_free_mem(&buf);
-                       return (EXT2_ET_DIR_CORRUPTED);
-               }
-               p += rec_len;
-               dirent->inode = ext2fs_swab32(dirent->inode);
-               dirent->rec_len = ext2fs_swab16(dirent->rec_len);
-               dirent->name_len = ext2fs_swab16(dirent->name_len);
-
-               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
-                       dirent->name_len = ext2fs_swab16(dirent->name_len);
-       }
+       retval = ext2fs_dirent_swab_out(fs, buf, flags);
+       if (retval)
+               return retval;
+#endif
+       retval = ext2fs_dir_block_csum_set(fs, ino,
+                                          (struct ext2_dir_entry *)buf);
+       if (retval)
+               goto out;
+
        retval = io_channel_write_blk64(fs->io, block, 1, buf);
+
+out:
+#ifdef WORDS_BIGENDIAN
        ext2fs_free_mem(&buf);
-       return retval;
-#else
-       return io_channel_write_blk64(fs->io, block, 1, (char *) inbuf);
 #endif
+       return retval;
+}
+
+errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
+                                 void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+       return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0);
 }
 
 errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
index 41c40882f6e3769a3d61f22bafbf35aa8ec5cc2e..22558d66ca02d0f31f387808e094fc6dc6742cce 100644 (file)
@@ -24,6 +24,7 @@ struct expand_dir_struct {
        int             newblocks;
        blk64_t         goal;
        errcode_t       err;
+       ext2_ino_t      dir;
 };
 
 static int expand_dir_proc(ext2_filsys fs,
@@ -62,7 +63,8 @@ static int expand_dir_proc(ext2_filsys        fs,
                        return BLOCK_ABORT;
                }
                es->done = 1;
-               retval = ext2fs_write_dir_block(fs, new_blk, block);
+               retval = ext2fs_write_dir_block4(fs, new_blk, block, 0,
+                                                es->dir);
        } else {
                retval = ext2fs_get_mem(fs->blocksize, &block);
                if (retval) {
@@ -110,6 +112,7 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
        es.err = 0;
        es.goal = 0;
        es.newblocks = 0;
+       es.dir = dir;
 
        retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
                                       0, expand_dir_proc, &es);
index 7289ae1c22d0751e2742bf0876d9acf8514c0aa3..430d5670bbe8bfab3d75212c5fc12238b70d4c01 100644 (file)
@@ -458,4 +458,7 @@ ec  EXT2_ET_EXTENT_CSUM_INVALID,
 ec     EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
        "Directory block does not have space for checksum"
 
+ec     EXT2_ET_DIR_CSUM_INVALID,
+       "Directory block checksum does not match directory block"
+
        end
index ba4521ad8da54ff51b433acb20bec155b31f9f58..cc4739907e2c575274c7e0760bdee468cfee723f 100644 (file)
@@ -943,6 +943,18 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len);
 extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);
 
 /* csum.c */
+#define EXT2_DIRENT_TAIL(block, blocksize) \
+       ((struct ext2_dir_entry_tail *)(((void *)(block)) + \
+       (blocksize) - sizeof(struct ext2_dir_entry_tail)))
+
+extern void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+                                         struct ext2_dir_entry_tail *t);
+extern int ext2fs_dirent_has_tail(ext2_filsys fs,
+                                 struct ext2_dir_entry *dirent);
+extern int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+                                       struct ext2_dir_entry *dirent);
+extern errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+                                          struct ext2_dir_entry *dirent);
 extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
                                          struct ext2_dir_entry *dirent,
                                          struct ext2_dx_countlimit **cc,
@@ -1025,12 +1037,16 @@ extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
                                        void *buf, int flags);
 extern errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
                                        void *buf, int flags);
+extern errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+                                       void *buf, int flags, ext2_ino_t ino);
 extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
                                        void *buf);
 extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
                                         void *buf, int flags);
 extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
                                         void *buf, int flags);
+extern errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+                                        void *buf, int flags, ext2_ino_t ino);
 
 /* dirhash.c */
 extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
@@ -1427,6 +1443,8 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
 extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
 
 /* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
 extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
                                 int has_header);
 extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header,
index 2d03b57313e9bce82c308e70e16f30e3f8501d36..2dec5dc2cacc053c0ad84c5f55373b19d7b0d1c6 100644 (file)
@@ -41,6 +41,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
        struct ext2_dir_entry *next;
        unsigned int rec_len, min_rec_len, curr_rec_len;
        int ret = 0;
+       int csum_size = 0;
 
        rec_len = EXT2_DIR_REC_LEN(ls->namelen);
 
@@ -48,12 +49,15 @@ static int link_proc(struct ext2_dir_entry *dirent,
        if (ls->err)
                return DIRENT_ABORT;
 
+       if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
+                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+               csum_size = sizeof(struct ext2_dir_entry_tail);
        /*
         * See if the following directory entry (if any) is unused;
         * if so, absorb it into this one.
         */
        next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
-       if ((offset + (int) curr_rec_len < blocksize - 8) &&
+       if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
            (next->inode == 0) &&
            (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
                curr_rec_len += next->rec_len;
index 861ddabbfd42d1e7108e691a29093223524f04d8..4a8543919fac7692a29b1ffd16041c43bf759e66 100644 (file)
@@ -100,7 +100,7 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        retval = ext2fs_write_new_inode(fs, ino, &inode);
        if (retval)
                goto cleanup;
-       retval = ext2fs_write_dir_block(fs, blk, block);
+       retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
        if (retval)
                goto cleanup;
 
index b0a1e4763334e4d78bbee08b3d26b598ee938223..2cd541d7f16bc3fb5b05b288f25ae62640beb5d2 100644 (file)
@@ -34,6 +34,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
        char                    *buf;
        int                     rec_len;
        int                     filetype = 0;
+       struct ext2_dir_entry_tail      *t;
+       int                     csum_size = 0;
 
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -43,7 +45,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
 
-       retval = 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_dir_entry_tail);
+
+       retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
        if (retval)
                return retval;
 
@@ -57,7 +63,7 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                dir->inode = dir_ino;
                dir->name_len = 1 | filetype;
                dir->name[0] = '.';
-               rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
+               rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
                dir->rec_len = EXT2_DIR_REC_LEN(1);
 
                /*
@@ -73,6 +79,11 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                dir->name[1] = '.';
 
        }
+
+       if (csum_size) {
+               t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
+               ext2fs_initialize_dirent_tail(fs, t);
+       }
        *block = buf;
        return 0;
 }
index 7c99373c3f2bfdfc513d95588e07c02b55581239..69916e59394a32426b4acab74d6b70c3ce272040 100644 (file)
@@ -350,4 +350,66 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
        mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
 }
 
+errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
+{
+       errcode_t       retval;
+       char            *p, *end;
+       struct ext2_dir_entry *dirent;
+       unsigned int    name_len, rec_len;
+
+       p = (char *) buf;
+       end = (char *) buf + fs->blocksize;
+       while (p < end-8) {
+               dirent = (struct ext2_dir_entry *) p;
+               dirent->inode = ext2fs_swab32(dirent->inode);
+               dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+               dirent->name_len = ext2fs_swab16(dirent->name_len);
+               name_len = dirent->name_len;
+               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+               retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+               if (retval)
+                       return retval;
+               if ((rec_len < 8) || (rec_len % 4)) {
+                       rec_len = 8;
+                       retval = EXT2_ET_DIR_CORRUPTED;
+               } else if (((name_len & 0xFF) + 8) > rec_len)
+                       retval = EXT2_ET_DIR_CORRUPTED;
+               p += rec_len;
+       }
+
+       return 0;
+}
+
+errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
+{
+       errcode_t       retval;
+       char            *p, *end;
+       unsigned int    rec_len;
+       struct ext2_dir_entry *dirent;
+
+       p = buf;
+       end = buf + fs->blocksize;
+       while (p < end) {
+               dirent = (struct ext2_dir_entry *) p;
+               retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+               if (retval)
+                       return retval;
+               if ((rec_len < 8) ||
+                   (rec_len % 4)) {
+                       ext2fs_free_mem(&buf);
+                       return EXT2_ET_DIR_CORRUPTED;
+               }
+               p += rec_len;
+               dirent->inode = ext2fs_swab32(dirent->inode);
+               dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+               dirent->name_len = ext2fs_swab16(dirent->name_len);
+
+               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+       }
+
+       return 0;
+}
+
 #endif