Hi,

  attached is a new version of support for 64KB blocksize in e2fsprogs. The
patch went through testing by a script I'll send in the following email so
now the modifications should be correct. Ted, can you have a look at it
when you have time? Thanks.

                                                                        Honza

-- 
Jan Kara <[EMAIL PROTECTED]>
SUSE Labs, CR

---

Subject: Support for 64KB blocksize in ext2-4 directories.

When block size is 64KB, we have to take care that rec_len does not overflow.
Kernel stores 0xffff in case 0x10000 should be stored - perform appropriate
conversion when processing directories.

Signed-off-by: Jan Kara <[EMAIL PROTECTED]>

diff --git a/debugfs/htree.c b/debugfs/htree.c
index d0e673e..a326241 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -40,6 +40,7 @@ static void htree_dump_leaf_node(ext2_fi
        blk_t           pblk;
        ext2_dirhash_t  hash;
        int             hash_alg;
+       int rec_len;
        
        errcode = ext2fs_bmap(fs, ino, inode, buf, 0, blk, &pblk);
        if (errcode) {
@@ -61,10 +62,8 @@ static void htree_dump_leaf_node(ext2_fi
 
        while (offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (buf + offset);
-               if (((offset + dirent->rec_len) > fs->blocksize) ||
-                   (dirent->rec_len < 8) ||
-                   ((dirent->rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+               rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
+               if (ext2fs_validate_dirent(fs, offset, dirent) < 0) {
                        fprintf(pager, "Corrupted directory block (%u)!\n", 
blk);
                        break;
                }
@@ -79,7 +78,7 @@ static void htree_dump_leaf_node(ext2_fi
                        com_err("htree_dump_leaf_node", errcode,
                                "while calculating hash");
                sprintf(tmp, "%u 0x%08x (%d) %s   ", dirent->inode,
-                       hash, dirent->rec_len, name);
+                       hash, rec_len, name);
                thislen = strlen(tmp);
                if (col + thislen > 80) {
                        fprintf(pager, "\n");
@@ -87,7 +86,7 @@ static void htree_dump_leaf_node(ext2_fi
                }
                fprintf(pager, "%s", tmp);
                col += thislen;
-               offset += dirent->rec_len;
+               offset += rec_len;
        }
        fprintf(pager, "\n");
 }
@@ -389,7 +388,7 @@ static int search_dir_block(ext2_filsys
                        printf("offset %u\n", offset);
                        return BLOCK_ABORT;
                }
-               offset += dirent->rec_len;
+               offset += ext2fs_rec_len_from_disk(dirent->rec_len);
        }
        return 0;
 }
diff --git a/debugfs/ls.c b/debugfs/ls.c
index 52c7e34..1960c11 100644
--- a/debugfs/ls.c
+++ b/debugfs/ls.c
@@ -97,7 +97,7 @@ static int list_dir_proc(ext2_ino_t dir
                fprintf (ls->f, " %s %s\n", datestr, name);
        } else {
                sprintf(tmp, "%c%u%c (%d) %s   ", lbr, dirent->inode, rbr,
-                       dirent->rec_len, name);
+                       ext2fs_rec_len_from_disk(dirent->rec_len), name);
                thislen = strlen(tmp);
 
                if (ls->col + thislen > 80) {
diff --git a/e2fsck/message.c b/e2fsck/message.c
index b2e3e0f..05b2e17 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -342,12 +342,13 @@ static _INLINE_ void expand_dirent_expre
                                              struct problem_context *ctx)
 {
        struct ext2_dir_entry   *dirent;
-       int     len;
+       int     len, rec_len;
        
        if (!ctx || !ctx->dirent)
                goto no_dirent;
        
        dirent = ctx->dirent;
+       rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
        
        switch (ch) {
        case 'i':
@@ -357,12 +358,12 @@ static _INLINE_ void expand_dirent_expre
                len = dirent->name_len & 0xFF;
                if (len > EXT2_NAME_LEN)
                        len = EXT2_NAME_LEN;
-               if (len > dirent->rec_len)
-                       len = dirent->rec_len;
+               if (len > rec_len)
+                       len = rec_len;
                safe_print(dirent->name, len);
                break;
        case 'r':
-               printf("%u", dirent->rec_len);
+               printf("%u", rec_len);
                break;
        case 'l':
                printf("%u", dirent->name_len & 0xFF);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index ceb9c7f..fd2c7d0 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -379,6 +379,7 @@ static void check_is_really_dir(e2fsck_t
        errcode_t               retval;
        blk_t                   blk;
        int                     i, not_device = 0;
+       int                     rec_len;
 
        if (LINUX_S_ISDIR(inode->i_mode) || LINUX_S_ISREG(inode->i_mode) ||
            LINUX_S_ISLNK(inode->i_mode) || inode->i_block[0] == 0)
@@ -408,20 +409,22 @@ static void check_is_really_dir(e2fsck_t
                return;
 
        dirent = (struct ext2_dir_entry *) buf;
+       rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
        if (((dirent->name_len & 0xFF) != 1) ||
            (dirent->name[0] != '.') ||
            (dirent->inode != pctx->ino) ||
-           (dirent->rec_len < 12) ||
-           (dirent->rec_len % 4) ||
-           (dirent->rec_len >= ctx->fs->blocksize - 12))
+           (rec_len < 12) ||
+           (rec_len % 4) ||
+           (rec_len >= ctx->fs->blocksize - 12))
                return;
 
        dirent = (struct ext2_dir_entry *) (buf + dirent->rec_len);
+       rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
        if (((dirent->name_len & 0xFF) != 2) ||
            (dirent->name[0] != '.') ||
            (dirent->name[1] != '.') ||
-           (dirent->rec_len < 12) ||
-           (dirent->rec_len % 4))
+           (rec_len < 12) ||
+           (rec_len % 4))
                return;
 
        if (fix_problem(ctx, PR_1_TREAT_AS_DIRECTORY, pctx)) {
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 27f7136..3080326 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -365,6 +365,7 @@ static int check_dot(e2fsck_t ctx,
        int     created = 0;
        int     new_len;
        int     problem = 0;
+       int     rec_len;
        
        if (!dirent->inode)
                problem = PR_2_MISSING_DOT;
@@ -374,10 +375,11 @@ static int check_dot(e2fsck_t ctx,
        else if (dirent->name[1] != '\0')
                problem = PR_2_DOT_NULL_TERM;
        
+       rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
        if (problem) {
                if (fix_problem(ctx, problem, pctx)) {
-                       if (dirent->rec_len < 12)
-                               dirent->rec_len = 12;
+                       if (rec_len < 12)
+                               dirent->rec_len = ext2fs_rec_len_to_disk(12);
                        dirent->inode = ino;
                        dirent->name_len = 1;
                        dirent->name[0] = '.';
@@ -392,15 +394,15 @@ static int check_dot(e2fsck_t ctx,
                        status = 1;
                }
        }
-       if (dirent->rec_len > 12) {
-               new_len = dirent->rec_len - 12;
+       if (rec_len > 12) {
+               new_len = rec_len - 12;
                if (new_len > 12) {
                        if (created ||
                            fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
                                nextdir = (struct ext2_dir_entry *)
                                        ((char *) dirent + 12);
-                               dirent->rec_len = 12;
-                               nextdir->rec_len = new_len;
+                               dirent->rec_len = ext2fs_rec_len_to_disk(12);
+                               nextdir->rec_len = 
ext2fs_rec_len_to_disk(new_len);
                                nextdir->inode = 0;
                                nextdir->name_len = 0;
                                status = 1;
@@ -432,8 +434,8 @@ static int check_dotdot(e2fsck_t ctx,
 
        if (problem) {
                if (fix_problem(ctx, problem, pctx)) {
-                       if (dirent->rec_len < 12)
-                               dirent->rec_len = 12;
+                       if (ext2fs_rec_len_from_disk(dirent->rec_len) < 12)
+                               dirent->rec_len = ext2fs_rec_len_to_disk(12);
                        /*
                         * Note: we don't have the parent inode just
                         * yet, so we will fill it in with the root
@@ -652,14 +654,15 @@ static void salvage_directory(ext2_filsy
                              unsigned int *offset)
 {
        char    *cp = (char *) dirent;
-       int left = fs->blocksize - *offset - dirent->rec_len;
+       int rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
+       int left = fs->blocksize - *offset - rec_len;
        unsigned int name_len = dirent->name_len & 0xFF;
 
        /*
         * Special case of directory entry of size 8: copy what's left
         * of the directory block up to cover up the invalid hole.
         */
-       if ((left >= 12) && (dirent->rec_len == 8)) {
+       if ((left >= 12) && (rec_len == 8)) {
                memmove(cp, cp+8, left);
                memset(cp + left, 0, 8);
                return;
@@ -670,10 +673,10 @@ static void salvage_directory(ext2_filsy
         * record length.
         */
        if ((left < 0) &&
-           (name_len + 8 <= dirent->rec_len + (unsigned) left) &&
+           (name_len + 8 <= rec_len + (unsigned) left) &&
            dirent->inode <= fs->super->s_inodes_count &&
            strnlen(dirent->name, name_len) == name_len) {
-               dirent->rec_len += left;
+               dirent->rec_len = ext2fs_rec_len_to_disk(rec_len + left);
                return;
        }
        /*
@@ -681,10 +684,11 @@ static void salvage_directory(ext2_filsy
         * of four, and not too big, such that it is valid, let the
         * previous directory entry absorb the invalid one.
         */
-       if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0 &&
-           (*offset + dirent->rec_len <= fs->blocksize)) {
-               prev->rec_len += dirent->rec_len;
-               *offset += dirent->rec_len;
+       if (prev && rec_len && (rec_len % 4) == 0 &&
+           (*offset + rec_len <= fs->blocksize)) {
+               prev->rec_len = ext2fs_rec_len_to_disk(
+                       ext2fs_rec_len_from_disk(prev->rec_len) + rec_len);
+               *offset += rec_len;
                return;
        }
        /*
@@ -694,10 +698,13 @@ static void salvage_directory(ext2_filsy
         * new empty directory entry the rest of the directory block.
         */
        if (prev) {
-               prev->rec_len += fs->blocksize - *offset;
+               prev->rec_len = ext2fs_rec_len_to_disk(
+                       ext2fs_rec_len_from_disk(prev->rec_len) +
+                       fs->blocksize - *offset);
                *offset = fs->blocksize;
        } else {
-               dirent->rec_len = fs->blocksize - *offset;
+               dirent->rec_len = ext2fs_rec_len_to_disk(
+                       fs->blocksize - *offset);
                dirent->name_len = 0;
                dirent->inode = 0;
        }
@@ -731,6 +738,7 @@ static int check_dir_block(ext2_filsys f
        struct problem_context  pctx;
        int     dups_found = 0;
        int     ret;
+       int     rec_len;
 
        cd = (struct check_dir_struct *) priv_data;
        buf = cd->buf;
@@ -802,6 +810,7 @@ static int check_dir_block(ext2_filsys f
                dx_db->max_hash = 0;
                        
                dirent = (struct ext2_dir_entry *) buf;
+               rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
                limit = (struct ext2_dx_countlimit *) (buf+8);
                if (db->blockcnt == 0) {
                        root = (struct ext2_dx_root_info *) (buf + 24);
@@ -821,7 +830,7 @@ static int check_dir_block(ext2_filsys f
                                dx_dir->hashversion += 3;
                        dx_dir->depth = root->indirect_levels + 1;
                } else if ((dirent->inode == 0) &&
-                          (dirent->rec_len == fs->blocksize) &&
+                          (rec_len == fs->blocksize) &&
                           (dirent->name_len == 0) &&
                           (ext2fs_le16_to_cpu(limit->limit) == 
                            ((fs->blocksize-8) / 
@@ -835,12 +844,13 @@ static int check_dir_block(ext2_filsys f
        do {
                problem = 0;
                dirent = (struct ext2_dir_entry *) (buf + offset);
+               rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
                cd->pctx.dirent = dirent;
                cd->pctx.num = offset;
-               if (((offset + dirent->rec_len) > fs->blocksize) ||
-                   (dirent->rec_len < 12) ||
-                   ((dirent->rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+               if (((offset + rec_len) > fs->blocksize) ||
+                   (rec_len < 12) ||
+                   ((rec_len % 4) != 0) ||
+                   (((dirent->name_len & 0xFF)+8) > rec_len)) {
                        if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
                                salvage_directory(fs, dirent, prev, &offset);
                                dir_modified++;
@@ -1035,7 +1045,7 @@ static int check_dir_block(ext2_filsys f
                ctx->fs_total_count++;
        next:
                prev = dirent;
-               offset += dirent->rec_len;
+               offset += ext2fs_rec_len_from_disk(dirent->rec_len);
                dot_state++;
        } while (offset < fs->blocksize);
 #if 0
@@ -1055,9 +1065,10 @@ static int check_dir_block(ext2_filsys f
        }
 #endif /* ENABLE_HTREE */
        if (offset != fs->blocksize) {
-               cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
+               cd->pctx.num = ext2fs_rec_len_from_disk(dirent->rec_len)
+                                - fs->blocksize + offset;
                if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
-                       dirent->rec_len = cd->pctx.num;
+                       dirent->rec_len = ext2fs_rec_len_to_disk(cd->pctx.num);
                        dir_modified++;
                }
        }
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index 8c1459c..2b70f98 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -102,7 +102,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;
+               dirent->rec_len = ext2fs_rec_len_to_disk(fs->blocksize);
        } else {
                fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
                if (fd->err)
@@ -116,14 +116,11 @@ static int fill_dir_block(ext2_filsys fs
        dir_offset = 0;
        while (dir_offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (dir + dir_offset);
-               if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
-                   (dirent->rec_len < 8) ||
-                   ((dirent->rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+               if (ext2fs_validate_dirent(fs, dir_offset, dirent) < 0) {
                        fd->err = EXT2_ET_DIR_CORRUPTED;
                        return BLOCK_ABORT;
                }
-               dir_offset += dirent->rec_len;
+               dir_offset += ext2fs_rec_len_from_disk(dirent->rec_len);
                if (dirent->inode == 0)
                        continue;
                if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
@@ -416,7 +413,9 @@ static errcode_t copy_dir_entries(ext2_f
                rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
                if (rec_len > left) {
                        if (left)
-                               dirent->rec_len += left;
+                               dirent->rec_len = ext2fs_rec_len_to_disk(
+                                       
ext2fs_rec_len_from_disk(dirent->rec_len) +
+                                       left);
                        if ((retval = get_next_block(fs, outdir,
                                                      &block_start)))
                                return retval;
@@ -432,19 +431,20 @@ static errcode_t copy_dir_entries(ext2_f
                }
                dirent->inode = ent->dir->inode;
                dirent->name_len = ent->dir->name_len;
-               dirent->rec_len = rec_len;
+               dirent->rec_len = ext2fs_rec_len_to_disk(rec_len);
                memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
                offset += rec_len;
                left -= rec_len;
                if (left < 12) {
-                       dirent->rec_len += left;
+                       dirent->rec_len = ext2fs_rec_len_to_disk(rec_len + 
left);
                        offset += left;
                        left = 0;
                }
                prev_hash = ent->hash;
        }
        if (left)
-               dirent->rec_len += left;
+               dirent->rec_len = ext2fs_rec_len_to_disk(
+                       ext2fs_rec_len_from_disk(dirent->rec_len) + left);
 
        return 0;
 }
@@ -466,13 +466,13 @@ static struct ext2_dx_root_info *set_roo
        dir->inode = ino;
        dir->name[0] = '.';
        dir->name_len = 1 | filetype;
-       dir->rec_len = 12;
+       dir->rec_len = ext2fs_rec_len_to_disk(12);
        dir = (struct ext2_dir_entry *) (buf + 12);
        dir->inode = parent;
        dir->name[0] = '.';
        dir->name[1] = '.';
        dir->name_len = 2 | filetype;
-       dir->rec_len = fs->blocksize - 12;
+       dir->rec_len = ext2fs_rec_len_to_disk(fs->blocksize - 12);
        
        root = (struct ext2_dx_root_info *) (buf+24);
        root->reserved_zero = 0;
@@ -497,7 +497,7 @@ static struct ext2_dx_entry *set_int_nod
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
        dir->inode = 0;
-       dir->rec_len = fs->blocksize;
+       dir->rec_len = ext2fs_rec_len_to_disk(fs->blocksize);
        
        limits = (struct ext2_dx_countlimit *) (buf+8);
        limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
diff --git a/ext2ed/dir_com.c b/ext2ed/dir_com.c
index c6b194e..d36d4b9 100644
--- a/ext2ed/dir_com.c
+++ b/ext2ed/dir_com.c
@@ -117,7 +117,7 @@ struct struct_file_info search_dir_entri
                dir_entry_ptr=(struct ext2_dir_entry_2 *) 
(info.buffer+info.dir_entry_offset);
 
                info.dir_entry_num++;
-               next = dir_entry_ptr->rec_len;
+               next = ext2_rec_len_from_disk(dir_entry_ptr->rec_len);
                if (!next)
                        next = file_system_info.block_size - 
info.dir_entry_offset;
                info.dir_entry_offset += next;
@@ -463,7 +463,7 @@ Show the current search entry (info) in
        if (dir_entry_ptr->name_len > (COLS - 55) && COLS > 55)
                temp [COLS-55]=0;
        wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name 
= %s\n", /* Display the various fields */
-                
dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);
+                
dir_entry_ptr->inode,ext2fs_rec_len_from_disk(dir_entry_ptr->rec_len),dir_entry_ptr->name_len,temp);
 
        show_pad_info.max_line++;
 
@@ -619,8 +619,8 @@ because it is of variable length.
 
        if (strcasecmp ("rec_len",variable)==0) {
                found=1;
-               dir_entry_ptr->rec_len=(unsigned int) atol (value);
-               wprintw (command_win,"Variable %s set to 
%lu\n",variable,dir_entry_ptr->rec_len);refresh_command_win ();
+               dir_entry_ptr->rec_len=ext2fs_rec_len_to_disk((unsigned int) 
atol (value));
+               wprintw (command_win,"Variable %s set to 
%lu\n",variable,ext2fs_rec_len_from_disk(dir_entry_ptr->rec_len));refresh_command_win
 ();
 
        }
 
@@ -648,7 +648,7 @@ because it is of variable length.
                temp [dir_entry_ptr->name_len]=0;
                wmove (show_pad,file_info.dir_entry_num,0);
                wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = 
%-3lu name = %s\n",
-                        
dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);
+                        
dir_entry_ptr->inode,ext2fs_rec_len_from_disk(dir_entry_ptr->rec_len),dir_entry_ptr->name_len,temp);
                wattrset (show_pad,A_NORMAL);
                
show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2;
                refresh_show_pad ();
diff --git a/ext2ed/disk.c b/ext2ed/disk.c
index d29c719..b602724 100644
--- a/ext2ed/disk.c
+++ b/ext2ed/disk.c
@@ -210,7 +210,7 @@ Just read from the current position into
                
        if (current_type!=NULL)
                if (strcmp (current_type->name,"ext2_dir_entry")==0)
-                       
current_type->length=type_data.u.t_ext2_dir_entry.rec_len;
+                       
current_type->length=ext2_rec_len_from_disk(type_data.u.t_ext2_dir_entry.rec_len);
 
        return (1);
 }
diff --git a/ext2ed/ext2ed.h b/ext2ed/ext2ed.h
index deae516..7eb5b29 100644
--- a/ext2ed/ext2ed.h
+++ b/ext2ed/ext2ed.h
@@ -35,6 +35,7 @@ Copyright (C) 1995 Gadi Oxman
 #define DEBUG                                          /* Activate self-sanity 
checks */
 
 #include <ext2fs/ext2_fs.h>                            /* Main kernel ext2 
include file */
+#include <ext2fs/ext2fs.h>
 #include <sys/stat.h>
 
 #include <ncurses.h>
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 003c0a3..63ea974 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -35,10 +35,8 @@ static int ext2fs_validate_entry(char *b
        
        while (offset < final_offset) {
                dirent = (struct ext2_dir_entry *)(buf + offset);
-               offset += dirent->rec_len;
-               if ((dirent->rec_len < 8) ||
-                   ((dirent->rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
+               offset += ext2fs_rec_len_from_disk(dirent->rec_len);
+               if (ext2fs_validate_dirent(NULL, 0, dirent) < 0)
                        return 0;
        }
        return (offset == final_offset);
@@ -145,6 +143,7 @@ int ext2fs_process_dir_block(ext2_filsys
        int             changed = 0;
        int             do_abort = 0;
        int             entry, size;
+       int             rec_len;
        struct ext2_dir_entry *dirent;
 
        if (blockcnt < 0)
@@ -158,10 +157,7 @@ int ext2fs_process_dir_block(ext2_filsys
 
        while (offset < fs->blocksize) {
                dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
-               if (((offset + dirent->rec_len) > fs->blocksize) ||
-                   (dirent->rec_len < 8) ||
-                   ((dirent->rec_len % 4) != 0) ||
-                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+               if (ext2fs_validate_dirent(fs, offset, dirent) < 0) {
                        ctx->errcode = EXT2_ET_DIR_CORRUPTED;
                        return BLOCK_ABORT;
                }
@@ -185,16 +181,17 @@ int ext2fs_process_dir_block(ext2_filsys
                        break;
                }
 next:          
+               rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
                if (next_real_entry == offset)
-                       next_real_entry += dirent->rec_len;
+                       next_real_entry += rec_len;
  
                if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
                        size = ((dirent->name_len & 0xFF) + 11) & ~3;
 
-                       if (dirent->rec_len != size)  {
+                       if (rec_len != size)  {
                                unsigned int final_offset;
 
-                               final_offset = offset + dirent->rec_len;
+                               final_offset = offset + rec_len;
                                offset += size;
                                while (offset < final_offset &&
                                       !ext2fs_validate_entry(ctx->buf,
@@ -204,7 +201,7 @@ next:
                                continue;
                        }
                }
-               offset += dirent->rec_len;
+               offset += rec_len;
        }
 
        if (changed) {
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
index fb20fa0..545a22b 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -25,7 +25,7 @@ errcode_t ext2fs_read_dir_block2(ext2_fi
        errcode_t       retval;
        char            *p, *end;
        struct ext2_dir_entry *dirent;
-       unsigned int    name_len, rec_len;
+       unsigned int    rec_len;
        
 
        retval = io_channel_read_blk(fs->io, block, 1, buf);
@@ -39,20 +39,23 @@ errcode_t ext2fs_read_dir_block2(ext2_fi
 #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;
-#ifdef WORDS_BIGENDIAN
-               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+               if (!(flags & EXT2_DIRBLOCK_V2_STRUCT))
                        dirent->name_len = ext2fs_swab16(dirent->name_len);
 #endif
-               rec_len = dirent->rec_len;
-               if ((rec_len < 8) || (rec_len % 4)) {
-                       rec_len = 8;
-                       retval = EXT2_ET_DIR_CORRUPTED;
+               rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
+               switch (ext2fs_validate_dirent(fs, p - (char *)buf, dirent)) {
+                       case -1:
+                               rec_len = end - p;
+                               retval = EXT2_ET_DIR_CORRUPTED;
+                               break;
+                       case -2:
+                               rec_len = 8;
+                               retval = EXT2_ET_DIR_CORRUPTED;
+                               break;
+                       case -3:
+                               retval = EXT2_ET_DIR_CORRUPTED;
+                               break;
                }
-               if (((name_len & 0xFF) + 8) > dirent->rec_len)
-                       retval = EXT2_ET_DIR_CORRUPTED;
                p += rec_len;
        }
        return retval;
@@ -73,6 +76,7 @@ errcode_t ext2fs_write_dir_block2(ext2_f
        char            *p, *end;
        char            *buf = 0;
        struct ext2_dir_entry *dirent;
+       int rec_len;
 
        retval = ext2fs_get_mem(fs->blocksize, &buf);
        if (retval)
@@ -82,17 +86,14 @@ errcode_t ext2fs_write_dir_block2(ext2_f
        end = buf + fs->blocksize;
        while (p < end) {
                dirent = (struct ext2_dir_entry *) p;
-               if ((dirent->rec_len < 8) ||
-                   (dirent->rec_len % 4)) {
+               if (ext2fs_validate_dirent(fs, p-buf, dirent) < 0) {
                        ext2fs_free_mem(&buf);
                        return (EXT2_ET_DIR_CORRUPTED);
                }
-               p += dirent->rec_len;
+               p += ext2fs_rec_len_from_disk(dirent->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)
+               if (!(flags & EXT2_DIRBLOCK_V2_STRUCT))
                        dirent->name_len = ext2fs_swab16(dirent->name_len);
        }
        retval = io_channel_write_blk(fs->io, block, 1, buf);
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 36e7c8c..7b31c8b 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -724,6 +724,21 @@ struct ext2_dir_entry_2 {
 #define EXT2_DIR_ROUND                 (EXT2_DIR_PAD - 1)
 #define EXT2_DIR_REC_LEN(name_len)     (((name_len) + 8 + EXT2_DIR_ROUND) & \
                                         ~EXT2_DIR_ROUND)
+#define EXT2_MAX_REC_LEN               ((1<<16)-1)
+
+static inline unsigned ext2fs_rec_len_from_disk(unsigned len)
+{
+       if (len == EXT2_MAX_REC_LEN)
+               return 1 << 16;
+       return len;
+}
+
+static inline unsigned ext2fs_rec_len_to_disk(unsigned len)
+{
+       if (len == (1 << 16))
+               return EXT2_MAX_REC_LEN;
+       return len;
+}
 
 /*
  * This structure will be used for multiple mount protection. It will be
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index d691c1b..8b08c07 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1003,6 +1003,8 @@ extern blk_t ext2fs_group_last_block(ext
 extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
                                      struct ext2_inode *inode);
 extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b);
+extern int ext2fs_validate_dirent(ext2_filsys fs, unsigned int offset,
+                                struct ext2_dir_entry *dirent);
 
 /*
  * The actual inlined functions definitions themselves...
@@ -1203,6 +1205,24 @@ _INLINE_ unsigned int ext2fs_div_ceil(un
                return 0;
        return ((a - 1) / b) + 1;
 }
+
+/*
+ * Check whether directory entry is valid
+ */
+_INLINE_ int ext2fs_validate_dirent(ext2_filsys fs, unsigned int offset,
+                                struct ext2_dir_entry *dirent)
+{
+       int rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
+
+       if (fs && offset + rec_len > fs->blocksize)
+               return -1;
+       if (rec_len < 8 || rec_len % 4 != 0)
+               return -2;
+       if ((dirent->name_len & 0xFF)+8 > rec_len)
+               return -3;
+       return 0;
+}
+
 #undef _INLINE_
 #endif
 
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 5e0f4f3..0838599 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -35,21 +35,26 @@ static int link_proc(struct ext2_dir_ent
 {
        struct link_struct *ls = (struct link_struct *) priv_data;
        struct ext2_dir_entry *next;
-       int rec_len, min_rec_len;
+       int rec_len, min_rec_len, n_rec_len, c_rec_len;
        int ret = 0;
 
        rec_len = EXT2_DIR_REC_LEN(ls->namelen);
+       c_rec_len = ext2fs_rec_len_from_disk(dirent->rec_len);
 
        /*
         * See if the following directory entry (if any) is unused;
         * if so, absorb it into this one.
         */
-       next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
-       if ((offset + dirent->rec_len < blocksize - 8) &&
-           (next->inode == 0) &&
-           (offset + dirent->rec_len + next->rec_len <= blocksize)) {
-               dirent->rec_len += next->rec_len;
-               ret = DIRENT_CHANGED;
+       next = (struct ext2_dir_entry *) (buf + offset + c_rec_len);
+       if ((offset + c_rec_len < blocksize - 8) &&
+           (next->inode == 0)) {
+               n_rec_len = ext2fs_rec_len_from_disk(next->rec_len);
+               if (offset + c_rec_len + n_rec_len <= blocksize) {
+                       dirent->rec_len =
+                           ext2fs_rec_len_to_disk(c_rec_len + n_rec_len);
+                       c_rec_len += n_rec_len;
+                       ret = DIRENT_CHANGED;
+               }
        }
 
        /*
@@ -59,15 +64,15 @@ static int link_proc(struct ext2_dir_ent
         */
        if (dirent->inode) {
                min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
-               if (dirent->rec_len < (min_rec_len + rec_len))
+               if (c_rec_len < (min_rec_len + rec_len))
                        return ret;
-               rec_len = dirent->rec_len - min_rec_len;
-               dirent->rec_len = min_rec_len;
+               rec_len = c_rec_len - min_rec_len;
+               dirent->rec_len = ext2fs_rec_len_to_disk(min_rec_len);
                next = (struct ext2_dir_entry *) (buf + offset +
-                                                 dirent->rec_len);
+                                                 min_rec_len);
                next->inode = 0;
                next->name_len = 0;
-               next->rec_len = rec_len;
+               next->rec_len = ext2fs_rec_len_to_disk(rec_len);
                return DIRENT_CHANGED;
        }
 
@@ -75,7 +80,7 @@ static int link_proc(struct ext2_dir_ent
         * If we get this far, then the directory entry is not used.
         * See if we can fit the request entry in.  If so, do it.
         */
-       if (dirent->rec_len < rec_len)
+       if (c_rec_len < rec_len)
                return ret;
        dirent->inode = ls->inode;
        dirent->name_len = ls->namelen;
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index 3904d91..6a4b478 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -41,7 +41,7 @@ errcode_t ext2fs_new_dir_block(ext2_fils
                return retval;
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
-       dir->rec_len = fs->blocksize;
+       dir->rec_len = ext2fs_rec_len_to_disk(fs->blocksize);
 
        if (dir_ino) {
                if (fs->super->s_feature_incompat &
@@ -53,14 +53,16 @@ errcode_t ext2fs_new_dir_block(ext2_fils
                dir->inode = dir_ino;
                dir->name_len = 1 | filetype;
                dir->name[0] = '.';
-               rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
-               dir->rec_len = EXT2_DIR_REC_LEN(1);
+               rec_len = ext2fs_rec_len_from_disk(dir->rec_len)
+                               - EXT2_DIR_REC_LEN(1);
+               dir->rec_len = ext2fs_rec_len_to_disk(EXT2_DIR_REC_LEN(1));
 
                /*
                 * Set up entry for '..'
                 */
-               dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
-               dir->rec_len = rec_len;
+               dir = (struct ext2_dir_entry *) (buf +
+                       ext2fs_rec_len_from_disk(dir->rec_len));
+               dir->rec_len = ext2fs_rec_len_to_disk(rec_len);
                dir->inode = parent_ino;
                dir->name_len = 2 | filetype;
                dir->name[0] = '.';
diff --git a/lib/ext2fs/unlink.c b/lib/ext2fs/unlink.c
index 31dd8ec..f5f34dc 100644
--- a/lib/ext2fs/unlink.c
+++ b/lib/ext2fs/unlink.c
@@ -57,7 +57,12 @@ static int unlink_proc(struct ext2_dir_e
        }
 
        if (offset) 
-               prev->rec_len += dirent->rec_len;
+               /* We actually would not need to convert initial values as
+                * they are certainly less than 64K but let's not try to be
+                * too clever */
+               prev->rec_len = ext2fs_rec_len_to_disk(
+                       ext2fs_rec_len_from_disk(prev->rec_len) +
+                       ext2fs_rec_len_from_disk(dirent->rec_len));
        else
                dirent->inode = 0;
        ls->done++;
diff --git a/misc/e2image.c b/misc/e2image.c
index 1fbb267..3006af2 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -345,25 +345,29 @@ static void scramble_dir_block(ext2_fils
        end = buf + fs->blocksize;
        for (p = buf; p < end-8; p += rec_len) {
                dirent = (struct ext2_dir_entry_2 *) p;
-               rec_len = dirent->rec_len;
 #ifdef WORDS_BIGENDIAN
-               rec_len = ext2fs_swab16(rec_len);
+               rec_len = ext2fs_swab16(dirent->rec_len);
+#else
+               rec_len = dirent->rec_len;
 #endif
+               rec_len = ext2fs_rec_len_from_disk(rec_len);
 #if 0
                printf("rec_len = %d, name_len = %d\n", rec_len, 
dirent->name_len);
 #endif
-               if (rec_len < 8 || (rec_len % 4) ||
-                   (p+rec_len > end)) {
+               switch (ext2fs_validate_dirent(fs, p - buf, (struct 
ext2_dir_entry *)dirent)) {
+                   case -1:
+                   case -2:
                        printf("Corrupt directory block %lu: "
                               "bad rec_len (%d)\n", (unsigned long) blk, 
                               rec_len);
-                       rec_len = end - p;
+                       rec_len = ext2fs_rec_len_to_disk(end - p);
 #ifdef WORDS_BIGENDIAN
-                               dirent->rec_len = ext2fs_swab16(rec_len);
+                       dirent->rec_len = ext2fs_swab16(rec_len);
+#else
+                       dirent->rec_len = rec_len;
 #endif
                        continue;
-               }
-               if (dirent->name_len + 8 > rec_len) {
+                   case -3:
                        printf("Corrupt directory block %lu: "
                               "bad name_len (%d)\n", (unsigned long) blk, 
                               dirent->name_len);
-
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to