Whamcloud - gitweb
Fix encoding for rec_len in directories for >= 64k blocksize file systems
authorTheodore Ts'o <tytso@mit.edu>
Mon, 22 Jun 2009 01:07:38 +0000 (21:07 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 22 Jun 2009 01:07:38 +0000 (21:07 -0400)
Previously e2fsprogs interpreted 0 for a rec_len of 65536 (which could
occur if the directory block is completely empty in 64k blocksize
filesystems), while the kernel interpreted 65535 to mean 65536.  The
kernel will accept both to mean 65536, and encodes 65535 to be 65536.
This commit changes e2fsprogs to match.

We add the encoding agreed upon for 128k and 256k filesystems, but we
don't enable support for these larger block sizes, since they haven't
been fully tested.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
14 files changed:
debian/e2fslibs.symbols
debugfs/htree.c
e2fsck/message.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/rehash.c
lib/ext2fs/dir_iterate.c
lib/ext2fs/dirblock.c
lib/ext2fs/ext2fs.h
lib/ext2fs/link.c
lib/ext2fs/newdir.c
misc/e2image.c
tests/f_dup3/expect.1
tests/f_dupfsblks/expect.1

index 4a375c9..3ee0bca 100644 (file)
@@ -99,6 +99,8 @@ libext2fs.so.2 e2fslibs #MINVER#
  ext2fs_dblist_sort@Base 1.37
  ext2fs_default_journal_size@Base 1.40
  ext2fs_descriptor_block_loc@Base 1.37
+ ext2fs_get_rec_len@Base 1.41.7
+ ext2fs_set_rec_len@Base 1.41.7
  ext2fs_dir_iterate2@Base 1.37
  ext2fs_dir_iterate@Base 1.37
  ext2fs_dirhash@Base 1.37
index afb7605..01e4ca5 100644 (file)
@@ -39,7 +39,8 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
        char            tmp[EXT2_NAME_LEN + 16];
        blk_t           pblk;
        ext2_dirhash_t  hash, minor_hash;
-       int             rec_len, hash_alg;
+       unsigned int    rec_len;
+       int             hash_alg;
 
        errcode = ext2fs_bmap(fs, ino, inode, buf, 0, blk, &pblk);
        if (errcode) {
@@ -64,8 +65,13 @@ static void htree_dump_leaf_node(ext2_filsys fs, ext2_ino_t ino,
 
        while (offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (buf + offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
+               if (errcode) {
+                       com_err("htree_dump_leaf_inode", errcode,
+                               "while getting rec_len for block %lu",
+                               (unsigned long) blk);
+                       return;
+               }
                if (((offset + rec_len) > fs->blocksize) ||
                    (rec_len < 8) ||
                    ((rec_len % 4) != 0) ||
@@ -386,7 +392,7 @@ static int search_dir_block(ext2_filsys fs, blk_t *blocknr,
        struct ext2_dir_entry *dirent;
        errcode_t               errcode;
        unsigned int            offset = 0;
-       int                     rec_len;
+       unsigned int            rec_len;
 
        if (blockcnt < 0)
                return 0;
@@ -402,8 +408,13 @@ static int search_dir_block(ext2_filsys fs, blk_t *blocknr,
 
        while (offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (p->buf + offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               errcode = ext2fs_get_rec_len(fs, dirent, &rec_len);
+               if (errcode) {
+                       com_err("htree_dump_leaf_inode", errcode,
+                               "while getting rec_len for block %lu",
+                               (unsigned long) *blocknr);
+                       return;
+               }
                if (dirent->inode &&
                    p->len == (dirent->name_len & 0xFF) &&
                    strncmp(p->search_name, dirent->name,
index 5158ed6..3f85916 100644 (file)
@@ -347,10 +347,11 @@ static _INLINE_ void expand_inode_expression(char ch,
 /*
  * This function expands '%dX' expressions
  */
-static _INLINE_ void expand_dirent_expression(char ch,
+static _INLINE_ void expand_dirent_expression(ext2_filsys fs, char ch,
                                              struct problem_context *ctx)
 {
        struct ext2_dir_entry   *dirent;
+       unsigned int rec_len;
        int     len;
 
        if (!ctx || !ctx->dirent)
@@ -366,12 +367,14 @@ static _INLINE_ void expand_dirent_expression(char ch,
                len = dirent->name_len & 0xFF;
                if (len > EXT2_NAME_LEN)
                        len = EXT2_NAME_LEN;
-               if (len > dirent->rec_len)
-                       len = dirent->rec_len;
+               if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) &&
+                   (len > rec_len))
+                       len = rec_len;
                safe_print(dirent->name, len);
                break;
        case 'r':
-               printf("%u", dirent->rec_len);
+               (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+               printf("%u", rec_len);
                break;
        case 'l':
                printf("%u", dirent->name_len & 0xFF);
@@ -490,7 +493,7 @@ void print_e2fsck_message(e2fsck_t ctx, const char *msg,
                        expand_inode_expression(*cp, pctx);
                } else if (cp[0] == '%' && cp[1] == 'D') {
                        cp += 2;
-                       expand_dirent_expression(*cp, pctx);
+                       expand_dirent_expression(fs, *cp, pctx);
                } else if ((cp[0] == '%')) {
                        cp++;
                        expand_percent_expression(fs, *cp, pctx);
index 46189c0..518c2ff 100644 (file)
@@ -434,8 +434,9 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
                return;
 
        dirent = (struct ext2_dir_entry *) buf;
-       rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
+       if (retval)
+               return;
        if (((dirent->name_len & 0xFF) != 1) ||
            (dirent->name[0] != '.') ||
            (dirent->inode != pctx->ino) ||
@@ -445,8 +446,9 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
                return;
 
        dirent = (struct ext2_dir_entry *) (buf + rec_len);
-       rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
+       if (retval)
+               return;
        if (((dirent->name_len & 0xFF) != 2) ||
            (dirent->name[0] != '.') ||
            (dirent->name[1] != '.') ||
index f5a326d..bb3813c 100644 (file)
@@ -352,9 +352,9 @@ static int check_dot(e2fsck_t ctx,
                     ext2_ino_t ino, struct problem_context *pctx)
 {
        struct ext2_dir_entry *nextdir;
+       unsigned int    rec_len, new_len;
        int     status = 0;
        int     created = 0;
-       int     rec_len, new_len;
        int     problem = 0;
 
        if (!dirent->inode)
@@ -365,8 +365,7 @@ static int check_dot(e2fsck_t ctx,
        else if (dirent->name[1] != '\0')
                problem = PR_2_DOT_NULL_TERM;
 
-       rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (problem) {
                if (fix_problem(ctx, problem, pctx)) {
                        if (rec_len < 12)
@@ -393,7 +392,8 @@ static int check_dot(e2fsck_t ctx,
                                nextdir = (struct ext2_dir_entry *)
                                        ((char *) dirent + 12);
                                dirent->rec_len = 12;
-                               nextdir->rec_len = new_len;
+                               (void) ext2fs_set_rec_len(ctx->fs, new_len,
+                                                         nextdir);
                                nextdir->inode = 0;
                                nextdir->name_len = 0;
                                status = 1;
@@ -423,8 +423,7 @@ static int check_dotdot(e2fsck_t ctx,
        else if (dirent->name[2] != '\0')
                problem = PR_2_DOT_DOT_NULL_TERM;
 
-       rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (problem) {
                if (fix_problem(ctx, problem, pctx)) {
                        if (rec_len < 12)
@@ -647,11 +646,11 @@ static void salvage_directory(ext2_filsys fs,
                              unsigned int *offset)
 {
        char    *cp = (char *) dirent;
-       int     left, rec_len;
+       int left;
+       unsigned int rec_len, prev_rec_len;
        unsigned int name_len = dirent->name_len & 0xFF;
 
-       rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
        left = fs->blocksize - *offset - rec_len;
 
        /*
@@ -669,10 +668,11 @@ static void salvage_directory(ext2_filsys fs,
         * record length.
         */
        if ((left < 0) &&
-           (name_len + 8 <= rec_len + (unsigned) left) &&
+           ((int) rec_len + left > 8) &&
+           (name_len + 8 <= (int) rec_len + left) &&
            dirent->inode <= fs->super->s_inodes_count &&
            strnlen(dirent->name, name_len) == name_len) {
-               dirent->rec_len += left;
+               (void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent);
                return;
        }
        /*
@@ -682,7 +682,9 @@ static void salvage_directory(ext2_filsys fs,
         */
        if (prev && rec_len && (rec_len % 4) == 0 &&
            (*offset + rec_len <= fs->blocksize)) {
-               prev->rec_len += rec_len;
+               (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
+               prev_rec_len += rec_len;
+               (void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
                *offset += rec_len;
                return;
        }
@@ -693,10 +695,13 @@ static void salvage_directory(ext2_filsys fs,
         * new empty directory entry the rest of the directory block.
         */
        if (prev) {
-               prev->rec_len += fs->blocksize - *offset;
+               (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
+               prev_rec_len += fs->blocksize - *offset;
+               (void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
                *offset = fs->blocksize;
        } else {
-               dirent->rec_len = fs->blocksize - *offset;
+               rec_len = fs->blocksize - *offset;
+               (void) ext2fs_set_rec_len(fs, rec_len, dirent);
                dirent->name_len = 0;
                dirent->inode = 0;
        }
@@ -808,8 +813,7 @@ static int check_dir_block(ext2_filsys fs,
                dx_db->max_hash = 0;
 
                dirent = (struct ext2_dir_entry *) buf;
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
                limit = (struct ext2_dx_countlimit *) (buf+8);
                if (db->blockcnt == 0) {
                        root = (struct ext2_dx_root_info *) (buf + 24);
@@ -847,8 +851,7 @@ out_htree:
 
                problem = 0;
                dirent = (struct ext2_dir_entry *) (buf + offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
                cd->pctx.dirent = dirent;
                cd->pctx.num = offset;
                if (((offset + rec_len) > fs->blocksize) ||
@@ -1104,8 +1107,7 @@ out_htree:
        next:
                prev = dirent;
                if (dir_modified)
-                       rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                               dirent->rec_len : 65536;
+                       (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
                offset += rec_len;
                dot_state++;
        } while (offset < fs->blocksize);
index d2dbcce..50388f3 100644 (file)
@@ -88,8 +88,8 @@ static int fill_dir_block(ext2_filsys fs,
        struct hash_entry       *new_array, *ent;
        struct ext2_dir_entry   *dirent;
        char                    *dir;
-       unsigned int            offset, dir_offset;
-       int                     rec_len, hash_alg;
+       unsigned int            offset, dir_offset, rec_len;
+       int                     hash_alg;
 
        if (blockcnt < 0)
                return 0;
@@ -103,7 +103,7 @@ static int fill_dir_block(ext2_filsys fs,
        if (HOLE_BLKADDR(*block_nr)) {
                memset(dir, 0, fs->blocksize);
                dirent = (struct ext2_dir_entry *) dir;
-               dirent->rec_len = fs->blocksize;
+               (void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
        } else {
                fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
                if (fd->err)
@@ -117,8 +117,7 @@ static int fill_dir_block(ext2_filsys fs,
        dir_offset = 0;
        while (dir_offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (dir + dir_offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
                if (((dir_offset + rec_len) > fs->blocksize) ||
                    (rec_len < 8) ||
                    ((rec_len % 4) != 0) ||
@@ -404,7 +403,8 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        char                    *block_start;
        struct hash_entry       *ent;
        struct ext2_dir_entry   *dirent;
-       int                     i, rec_len, left;
+       unsigned int            rec_len, prev_rec_len;
+       int                     i, left;
        ext2_dirhash_t          prev_hash;
        int                     offset, slack;
 
@@ -429,6 +429,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
        if ((retval = get_next_block(fs, outdir, &block_start)))
                return retval;
        dirent = (struct ext2_dir_entry *) block_start;
+       prev_rec_len = 0;
        left = fs->blocksize;
        slack = fd->compress ? 12 :
                (fs->blocksize * ctx->htree_slack_percentage)/100;
@@ -440,8 +441,12 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                        continue;
                rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
                if (rec_len > left) {
-                       if (left)
-                               dirent->rec_len += left;
+                       if (left) {
+                               left += prev_rec_len;
+                               retval = ext2fs_set_rec_len(fs, left, dirent);
+                               if (retval)
+                                       return retval;
+                       }
                        if ((retval = get_next_block(fs, outdir,
                                                      &block_start)))
                                return retval;
@@ -457,21 +462,27 @@ static errcode_t copy_dir_entries(e2fsck_t ctx,
                }
                dirent->inode = ent->dir->inode;
                dirent->name_len = ent->dir->name_len;
-               dirent->rec_len = rec_len;
+               retval = ext2fs_set_rec_len(fs, rec_len, dirent);
+               if (retval)
+                       return retval;
+               prev_rec_len = rec_len;
                memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
                offset += rec_len;
                left -= rec_len;
                if (left < slack) {
-                       dirent->rec_len += left;
+                       prev_rec_len += left;
+                       retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent);
+                       if (retval)
+                               return retval;
                        offset += left;
                        left = 0;
                }
                prev_hash = ent->hash;
        }
        if (left)
-               dirent->rec_len += left;
+               retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
 
-       return 0;
+       return retval;
 }
 
 
@@ -522,7 +533,7 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
        dir->inode = 0;
-       dir->rec_len = fs->blocksize;
+       (void) ext2fs_set_rec_len(fs, fs->blocksize, dir);
 
        limits = (struct ext2_dx_countlimit *) (buf+8);
        limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
index 1f8cf8f..ac5a31e 100644 (file)
 #include "ext2_fs.h"
 #include "ext2fsP.h"
 
+#define EXT4_MAX_REC_LEN               ((1<<16)-1)
+
+errcode_t ext2fs_get_rec_len(ext2_filsys fs,
+                            struct ext2_dir_entry *dirent,
+                            unsigned int *rec_len)
+{
+       unsigned int len = dirent->rec_len;
+
+       if (len == EXT4_MAX_REC_LEN || len == 0)
+               *rec_len = fs->blocksize;
+       else 
+               *rec_len = (len & 65532) | ((len & 3) << 16);
+       return 0;
+}
+
+errcode_t ext2fs_set_rec_len(ext2_filsys fs,
+                            unsigned int len,
+                            struct ext2_dir_entry *dirent)
+{
+       if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3))
+               return EINVAL;
+       if (len < 65536) {
+               dirent->rec_len = len;
+               return 0;
+       }
+       if (len == fs->blocksize) {
+               if (fs->blocksize == 65536)
+                       dirent->rec_len = EXT4_MAX_REC_LEN;
+               else 
+                       dirent->rec_len = 0;
+       } else
+               dirent->rec_len = (len & 65532) | ((len >> 16) & 3);
+       return 0;
+}
+
 /*
  * This function checks to see whether or not a potential deleted
  * directory entry looks valid.  What we do is check the deleted entry
@@ -33,12 +68,12 @@ static int ext2fs_validate_entry(ext2_filsys fs, char *buf, int offset,
                                 int final_offset)
 {
        struct ext2_dir_entry *dirent;
-       int     rec_len;
+       unsigned int rec_len;
 
        while (offset < final_offset) {
                dirent = (struct ext2_dir_entry *)(buf + offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               if (ext2fs_get_rec_len(fs, dirent, &rec_len))
+                       return 0;
                offset += rec_len;
                if ((rec_len < 8) ||
                    ((rec_len % 4) != 0) ||
@@ -148,7 +183,8 @@ int ext2fs_process_dir_block(ext2_filsys fs,
        int             ret = 0;
        int             changed = 0;
        int             do_abort = 0;
-       int             rec_len, entry, size;
+       unsigned int    rec_len;
+       int             entry, size;
        struct ext2_dir_entry *dirent;
 
        if (blockcnt < 0)
@@ -162,8 +198,8 @@ int ext2fs_process_dir_block(ext2_filsys fs,
 
        while (offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               if (ext2fs_get_rec_len(fs, dirent, &rec_len))
+                       return BLOCK_ABORT;
                if (((offset + rec_len) > fs->blocksize) ||
                    (rec_len < 8) ||
                    ((rec_len % 4) != 0) ||
@@ -185,8 +221,8 @@ int ext2fs_process_dir_block(ext2_filsys fs,
                        entry++;
 
                if (ret & DIRENT_CHANGED) {
-                       rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                               dirent->rec_len : 65536;
+                       if (ext2fs_get_rec_len(fs, dirent, &rec_len))
+                               return BLOCK_ABORT;
                        changed++;
                }
                if (ret & DIRENT_ABORT) {
index 501c656..6542a81 100644 (file)
@@ -46,8 +46,8 @@ errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
                if (flags & EXT2_DIRBLOCK_V2_STRUCT)
                        dirent->name_len = ext2fs_swab16(dirent->name_len);
 #endif
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               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;
@@ -72,7 +72,7 @@ errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
        errcode_t       retval;
        char            *p, *end;
        char            *buf = 0;
-       int             rec_len;
+       unsigned int    rec_len;
        struct ext2_dir_entry *dirent;
 
        retval = ext2fs_get_mem(fs->blocksize, &buf);
@@ -83,8 +83,8 @@ errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
        end = buf + fs->blocksize;
        while (p < end) {
                dirent = (struct ext2_dir_entry *) p;
-               rec_len = (dirent->rec_len || fs->blocksize < 65536) ?
-                       dirent->rec_len : 65536;
+               if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0)
+                       return retval;
                if ((rec_len < 8) ||
                    (rec_len % 4)) {
                        ext2fs_free_mem(&buf);
index 08bfa03..234fbdd 100644 (file)
@@ -796,6 +796,12 @@ extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
 
 
 /* dir_iterate.c */
+extern errcode_t ext2fs_get_rec_len(ext2_filsys fs,
+                                   struct ext2_dir_entry *dirent,
+                                   unsigned int *rec_len);
+extern errcode_t ext2fs_set_rec_len(ext2_filsys fs,
+                                   unsigned int len,
+                                   struct ext2_dir_entry *dirent);
 extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
                              ext2_ino_t dir,
                              int flags,
index 5ed6394..7f2cfbc 100644 (file)
 #include "ext2fs.h"
 
 struct link_struct  {
+       ext2_filsys     fs;
        const char      *name;
        int             namelen;
        ext2_ino_t      inode;
        int             flags;
        int             done;
        unsigned int    blocksize;
+       errcode_t       err;
        struct ext2_super_block *sb;
 };
 
@@ -36,13 +38,14 @@ static int link_proc(struct ext2_dir_entry *dirent,
 {
        struct link_struct *ls = (struct link_struct *) priv_data;
        struct ext2_dir_entry *next;
-       int rec_len, min_rec_len, curr_rec_len;
+       unsigned int rec_len, min_rec_len, curr_rec_len;
        int ret = 0;
 
        rec_len = EXT2_DIR_REC_LEN(ls->namelen);
 
-       curr_rec_len = (dirent->rec_len || ls->blocksize < 65536) ?
-               dirent->rec_len : 65536;
+       ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
+       if (ls->err)
+               return DIRENT_ABORT;
 
        /*
         * See if the following directory entry (if any) is unused;
@@ -52,8 +55,10 @@ static int link_proc(struct ext2_dir_entry *dirent,
        if ((offset + curr_rec_len < blocksize - 8) &&
            (next->inode == 0) &&
            (offset + curr_rec_len + next->rec_len <= blocksize)) {
-               dirent->rec_len += next->rec_len;
-               curr_rec_len = dirent->rec_len;
+               curr_rec_len += next->rec_len;
+               ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
+               if (ls->err)
+                       return DIRENT_ABORT;
                ret = DIRENT_CHANGED;
        }
 
@@ -67,12 +72,16 @@ static int link_proc(struct ext2_dir_entry *dirent,
                if (curr_rec_len < (min_rec_len + rec_len))
                        return ret;
                rec_len = curr_rec_len - min_rec_len;
-               dirent->rec_len = min_rec_len;
+               ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
+               if (ls->err)
+                       return DIRENT_ABORT;
                next = (struct ext2_dir_entry *) (buf + offset +
                                                  dirent->rec_len);
                next->inode = 0;
                next->name_len = 0;
-               next->rec_len = rec_len;
+               ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
+               if (ls->err)
+                       return DIRENT_ABORT;
                return DIRENT_CHANGED;
        }
 
@@ -111,6 +120,7 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
        if (!(fs->flags & EXT2_FLAG_RW))
                return EXT2_ET_RO_FILSYS;
 
+       ls.fs = fs;
        ls.name = name;
        ls.namelen = name ? strlen(name) : 0;
        ls.inode = ino;
@@ -118,11 +128,14 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
        ls.done = 0;
        ls.sb = fs->super;
        ls.blocksize = fs->blocksize;
+       ls.err = 0;
 
        retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
                                    0, link_proc, &ls);
        if (retval)
                return retval;
+       if (ls.err)
+               return ls.err;
 
        if (!ls.done)
                return EXT2_ET_DIR_NO_SPACE;
index 4e7b40d..7f4266a 100644 (file)
@@ -41,7 +41,10 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                return retval;
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
-       dir->rec_len = fs->blocksize;
+
+       retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
+       if (retval)
+               return retval;
 
        if (dir_ino) {
                if (fs->super->s_feature_incompat &
@@ -60,7 +63,9 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                 * Set up entry for '..'
                 */
                dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
-               dir->rec_len = rec_len;
+               retval = ext2fs_set_rec_len(fs, rec_len, dir);
+               if (retval)
+                       return retval;
                dir->inode = parent_ino;
                dir->name_len = 2 | filetype;
                dir->name[0] = '.';
index dd2a1ca..83c1cca 100644 (file)
@@ -339,11 +339,14 @@ static void write_block(int fd, char *buf, int sparse_offset,
 
 int name_id[256];
 
+#define EXT4_MAX_REC_LEN               ((1<<16)-1)
+
 static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
 {
        char *p, *end, *cp;
        struct ext2_dir_entry_2 *dirent;
-       int rec_len, id, len;
+       unsigned int rec_len;
+       int id, len;
 
        end = buf + fs->blocksize;
        for (p = buf; p < end-8; p += rec_len) {
@@ -352,8 +355,10 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
 #ifdef WORDS_BIGENDIAN
                rec_len = ext2fs_swab16(rec_len);
 #endif
-               rec_len = (rec_len || fs->blocksize < 65536) ?
-                       rec_len : 65536;
+               if (rec_len == EXT4_MAX_REC_LEN || rec_len == 0)
+                       rec_len = fs->blocksize;
+               else 
+                       rec_len = (rec_len & 65532) | ((rec_len & 3) << 16);
 #if 0
                printf("rec_len = %d, name_len = %d\n", rec_len, dirent->name_len);
 #endif
@@ -363,8 +368,10 @@ static void scramble_dir_block(ext2_filsys fs, blk_t blk, char *buf)
                               "bad rec_len (%d)\n", (unsigned long) blk,
                               rec_len);
                        rec_len = end - p;
+                       (void) ext2fs_set_rec_len(fs, rec_len,
+                                       (struct ext2_dir_entry *) dirent);
 #ifdef WORDS_BIGENDIAN
-                               dirent->rec_len = ext2fs_swab16(rec_len);
+                       dirent->rec_len = ext2fs_swab16(dirent->rec_len);
 #endif
                        continue;
                }
index 1393416..9b1a28f 100644 (file)
@@ -24,8 +24,8 @@ File /e2fsck (inode #16, mod time Tue Sep 21 04:32:22 1993)
 Clone multiply-claimed blocks? yes
 
 Pass 2: Checking directory structure
-Directory inode 11, block 12, offset 0: directory corrupted
-Salvage? yes
+Entry '' in /lost+found (11) has invalid inode #: 24.
+Clear? yes
 
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
index 32ce89b..5003254 100644 (file)
@@ -35,14 +35,11 @@ File /quux (inode #14, mod time Thu Aug  5 07:18:09 1999)
 Clone multiply-claimed blocks? yes
 
 Pass 2: Checking directory structure
-Directory inode 12, block 1, offset 0: directory corrupted
-Salvage? yes
-
-Directory inode 12, block 2, offset 0: directory corrupted
-Salvage? yes
+Entry '' in ??? (12) has invalid inode #: 4194303.
+Clear? yes
 
-Directory inode 12, block 3, offset 0: directory corrupted
-Salvage? yes
+Entry 'M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?M-^?' in ??? (12) has invalid inode #: 16383.
+Clear? yes
 
 Entry '' in ??? (12) has a zero-length name.
 Clear? yes