aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2009-06-21 21:07:38 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-06-21 21:07:38 -0400
commit8a480350952f6f0fdbce54326b6d847e66368897 (patch)
tree8f09d26e06335f00dec8166acaae425cc30c3523
parent8a6ede8b7a8d5be0d49d6bfa7616537b61dfdc1b (diff)
downloade2fsprogs-8a480350952f6f0fdbce54326b6d847e66368897.tar.gz
e2fsprogs-8a480350952f6f0fdbce54326b6d847e66368897.tar.xz
e2fsprogs-8a480350952f6f0fdbce54326b6d847e66368897.zip
Fix encoding for rec_len in directories for >= 64k blocksize file systems
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>
-rw-r--r--debian/e2fslibs.symbols2
-rw-r--r--debugfs/htree.c23
-rw-r--r--e2fsck/message.c13
-rw-r--r--e2fsck/pass1.c10
-rw-r--r--e2fsck/pass2.c42
-rw-r--r--e2fsck/rehash.c37
-rw-r--r--lib/ext2fs/dir_iterate.c52
-rw-r--r--lib/ext2fs/dirblock.c10
-rw-r--r--lib/ext2fs/ext2fs.h6
-rw-r--r--lib/ext2fs/link.c27
-rw-r--r--lib/ext2fs/newdir.c9
-rw-r--r--misc/e2image.c15
-rw-r--r--tests/f_dup3/expect.14
-rw-r--r--tests/f_dupfsblks/expect.111
14 files changed, 178 insertions, 83 deletions
diff --git a/debian/e2fslibs.symbols b/debian/e2fslibs.symbols
index 4a375c9c..3ee0bca1 100644
--- a/debian/e2fslibs.symbols
+++ b/debian/e2fslibs.symbols
@@ -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
diff --git a/debugfs/htree.c b/debugfs/htree.c
index afb7605d..01e4ca52 100644
--- a/debugfs/htree.c
+++ b/debugfs/htree.c
@@ -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,
diff --git a/e2fsck/message.c b/e2fsck/message.c
index 5158ed64..3f859160 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 46189c0d..518c2ff8 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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] != '.') ||
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index f5a326d3..bb3813cd 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -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);
diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c
index d2dbcce1..50388f36 100644
--- a/e2fsck/rehash.c
+++ b/e2fsck/rehash.c
@@ -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);
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 1f8cf8ff..ac5a31e2 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -21,6 +21,41 @@
#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) {
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
index 501c6560..6542a81d 100644
--- a/lib/ext2fs/dirblock.c
+++ b/lib/ext2fs/dirblock.c
@@ -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);
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 08bfa03e..234fbdd2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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,
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 5ed63942..7f2cfbc6 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -19,12 +19,14 @@
#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;
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index 4e7b40d6..7f4266a5 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -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] = '.';
diff --git a/misc/e2image.c b/misc/e2image.c
index dd2a1caa..83c1cca9 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -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;
}
diff --git a/tests/f_dup3/expect.1 b/tests/f_dup3/expect.1
index 13934169..9b1a28fa 100644
--- a/tests/f_dup3/expect.1
+++ b/tests/f_dup3/expect.1
@@ -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
diff --git a/tests/f_dupfsblks/expect.1 b/tests/f_dupfsblks/expect.1
index 32ce89bb..5003254f 100644
--- a/tests/f_dupfsblks/expect.1
+++ b/tests/f_dupfsblks/expect.1
@@ -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