OSDN Git Service

libext2fs: add dx_root/dx_node checksum calculation and verification helpers
authorDarrick J. Wong <djwong@us.ibm.com>
Mon, 30 Jul 2012 23:22:04 +0000 (19:22 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 30 Jul 2012 23:22:04 +0000 (19:22 -0400)
Verify and calculate checksums of htree internal node blocks.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
lib/ext2fs/csum.c
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2fs.h

index 92345cf..262eb75 100644 (file)
 #define STATIC static
 #endif
 
+static __u16 do_nothing16(__u16 x)
+{
+       return x;
+}
+
+static __u16 disk_to_host16(__u16 x)
+{
+       return ext2fs_le16_to_cpu(x);
+}
+
+static errcode_t __get_dx_countlimit(ext2_filsys fs,
+                                    struct ext2_dir_entry *dirent,
+                                    struct ext2_dx_countlimit **cc,
+                                    int *offset,
+                                    int need_swab)
+{
+       struct ext2_dir_entry *dp;
+       struct ext2_dx_root_info *root;
+       struct ext2_dx_countlimit *c;
+       int count_offset, max_sane_entries;
+       unsigned int rec_len;
+       __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+       rec_len = translate(dirent->rec_len);
+
+       if (rec_len == fs->blocksize && translate(dirent->name_len) == 0)
+               count_offset = 8;
+       else if (rec_len == 12) {
+               dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len);
+               rec_len = translate(dp->rec_len);
+               if (rec_len != fs->blocksize - 12)
+                       return EXT2_ET_DB_NOT_FOUND;
+               root = (struct ext2_dx_root_info *)(((void *)dp + 12));
+               if (root->reserved_zero ||
+                   root->info_length != sizeof(struct ext2_dx_root_info))
+                       return EXT2_ET_DB_NOT_FOUND;
+               count_offset = 32;
+       } else
+               return EXT2_ET_DB_NOT_FOUND;
+
+       c = (struct ext2_dx_countlimit *)(((void *)dirent) + count_offset);
+       max_sane_entries = (fs->blocksize - count_offset) /
+                          sizeof(struct ext2_dx_entry);
+       if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries ||
+           ext2fs_le16_to_cpu(c->count) > max_sane_entries)
+               return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+       if (offset)
+               *offset = count_offset;
+       if (cc)
+               *cc = c;
+
+       return 0;
+}
+
+errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+                                  struct ext2_dir_entry *dirent,
+                                  struct ext2_dx_countlimit **cc,
+                                  int *offset)
+{
+       return __get_dx_countlimit(fs, dirent, cc, offset, 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,
+                               struct ext2_dx_tail *t)
+{
+       errcode_t retval;
+       char *buf = (char *)dirent;
+       int size;
+       __u32 old_csum, gen;
+       struct ext2_inode inode;
+
+       size = count_offset + (count * sizeof(struct ext2_dx_entry));
+       old_csum = t->dt_checksum;
+       t->dt_checksum = 0;
+
+       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);
+       *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t,
+                               sizeof(struct ext2_dx_tail));
+       t->dt_checksum = old_csum;
+
+       return 0;
+}
+
+static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+                                struct ext2_dir_entry *dirent)
+{
+       __u32 calculated;
+       errcode_t retval;
+       struct ext2_dx_countlimit *c;
+       struct ext2_dx_tail *t;
+       int count_offset, limit, count;
+
+       retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+       if (retval)
+               return 1;
+       limit = ext2fs_le16_to_cpu(c->limit);
+       count = ext2fs_le16_to_cpu(c->count);
+       if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+           fs->blocksize - sizeof(struct ext2_dx_tail))
+               return 0;
+       /* htree structs are accessed in LE order */
+       t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+       retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, count_offset,
+                               count, t);
+       if (retval)
+               return 0;
+
+       return ext2fs_le32_to_cpu(t->dt_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+                                   struct ext2_dir_entry *dirent)
+{
+       __u32 crc;
+       errcode_t retval = 0;
+       struct ext2_dx_countlimit *c;
+       struct ext2_dx_tail *t;
+       int count_offset, limit, count;
+
+       retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+       if (retval)
+               return retval;
+       limit = ext2fs_le16_to_cpu(c->limit);
+       count = ext2fs_le16_to_cpu(c->count);
+       if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+           fs->blocksize - sizeof(struct ext2_dx_tail))
+               return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+       t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+
+       /* htree structs are accessed in LE order */
+       retval = ext2fs_dx_csum(fs, inum, dirent, &crc, count_offset, count, t);
+       if (retval)
+               return retval;
+       t->dt_checksum = ext2fs_cpu_to_le32(crc);
+       return retval;
+}
+
 #define EXT3_EXTENT_TAIL_OFFSET(hdr)   (sizeof(struct ext3_extent_header) + \
        (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
 
index b15d8d9..7289ae1 100644 (file)
@@ -455,4 +455,7 @@ ec  EXT2_ET_INODE_BITMAP_CSUM_INVALID,
 ec     EXT2_ET_EXTENT_CSUM_INVALID,
        "Extent block checksum does not match extent block"
 
+ec     EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
+       "Directory block does not have space for checksum"
+
        end
index 080232e..ba4521a 100644 (file)
@@ -943,6 +943,10 @@ 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 */
+extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+                                         struct ext2_dir_entry *dirent,
+                                         struct ext2_dx_countlimit **cc,
+                                         int *offset);
 extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
                                              ext2_ino_t inum,
                                              struct ext3_extent_header *eh);