From: Jan Kara <jack@suse.cz>
To: tytso@mit.edu
Cc: linux-ext4@vger.kernel.org
Subject: [PATCH] e2fsprogs: Handle rec_len correctly for 64KB blocksize
Date: Thu, 6 Dec 2007 15:11:34 +0100 [thread overview]
Message-ID: <20071206141133.GA6396@duck.suse.cz> (raw)
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 <jack@suse.cz>
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 <jack@suse.cz>
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);
next reply other threads:[~2007-12-06 14:11 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-12-06 14:11 Jan Kara [this message]
[not found] <20071106113142.GA23689@duck.suse.cz>
2007-11-07 16:09 ` [PATCH] e2fsprogs: Handle rec_len correctly for 64KB blocksize Jan Kara
2007-11-11 0:37 ` Theodore Tso
2007-11-12 9:52 ` Jan Kara
2007-11-12 14:58 ` Theodore Tso
2007-11-12 16:10 ` Jan Kara
2007-11-13 15:08 ` Andreas Dilger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20071206141133.GA6396@duck.suse.cz \
--to=jack@suse.cz \
--cc=linux-ext4@vger.kernel.org \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.