]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
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 4a375c9cb9c8af8cfcf0884152b376b3e5b17b78..3ee0bca1ead061b7c77229e05199ccd81cb3aab0 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 afb7605df412ed446ace11f9cde8db7dfd013b7a..01e4ca52aae81f688cf81fd103e64d385681d9eb 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 5158ed64c4f587d04b2bca1ce10073fbd79a2a8c..3f859160c70b253a5879d5545ed3b759ccc340cc 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 46189c0d5d7a99999bd00fe8dd4a49741631cb5c..518c2ff812defdac1acc49957a2280abab04b223 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 f5a326d3285823e71b5076004b2732949ea1b38a..bb3813cd725ed856b23e4af85f09874a6d84e57a 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 d2dbcce127abf5b0902ed56f3f06933f1527a47e..50388f366201af5b594e25585fbd0c732563e014 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 1f8cf8ff90e648ea022570dbfb933690ba5aeca7..ac5a31e2b540b03f89fdabfc006f51fde70aeb2f 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 501c6560b15cd18a30c2c01a8489585336706c52..6542a81d6dcc50706dd2499453b3be597d598005 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 08bfa03e84c11972bec8d928a42c1ca5cf192280..234fbdd255f275b0f7b4add0d54c8caa1dcd2572 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 5ed63942c243fe59472ce549a408e6db9d151567..7f2cfbc63f8aca53cb93409a08f3f9b09ec7cf1b 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 4e7b40d62d78610c99bc60a62265226d17292dba..7f4266a5e93cf08b0943f6992fe6afd8abb2036c 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 dd2a1caabc4a18408cdf1d0a3710c918b9f30779..83c1cca93ba33131418125c1e5ecff012c6357cd 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 13934169f7e0cfd07df7f28a3ec748d5ae6dbf63..9b1a28fa4ed667dc617f1ada53731ef21a8a8ed5 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 32ce89bbebca291ebad7fdad5e6117a049830207..5003254fa71755be9716c68ea65bfdb630fa38b1 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