}
/* XXX should check that beginning matches a directory */
- root = (struct ext2_dx_root_info *) (block_buf + 24);
+ root = get_ext2_dx_root_info(fs, block_buf);
if ((root->reserved_zero || root->info_length < 8) &&
fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
return (int) (db_a->blockcnt - db_b->blockcnt);
}
+void ext2_fix_dirent_dirdata(struct ext2_dir_entry_2 *de)
+{
+ int i = 0, dirdatalen, rlen;
+ __u8 flags = 0, new_flag = 0;
+ __u8 de_flags = de->file_type & ~EXT2_FT_MASK;
+
+ while (i < 4) {
+ flags = new_flag | (1 << i) << 4;
+
+ if ((de_flags & flags) == flags) {
+ dirdatalen = ext2_get_dirent_dirdata_size(de, flags);
+ rlen = __EXT2_DIR_REC_LEN(de->name_len + dirdatalen);
+
+ if (rlen > de->rec_len)
+ break;
+
+ new_flag |= flags;
+ }
+ i++;
+ }
+
+ de->file_type = (de->file_type & EXT2_FT_MASK) | new_flag;
+}
+
+
+/*
+ * check for dirent data in ext3 dirent.
+ * return 0 if dirent data is ok.
+ * return 1 if dirent data does not exist.
+ * return 2 if dirent was modified due to error.
+ */
+int e2fsck_check_dirent_data(e2fsck_t ctx, struct ext2_dir_entry_2 *de,
+ unsigned int offset, struct problem_context *pctx)
+{
+ if (!(ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_DIRDATA)) {
+ if (de->file_type & ~EXT2_FT_MASK) {
+ /* clear dirent extra data flags. */
+ if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) {
+ de->file_type &= EXT2_FT_MASK;
+ return 2;
+ }
+ }
+ return 1;
+ }
+ if (de->file_type & ~EXT2_FT_MASK) {
+ if (de->rec_len >= EXT2_DIR_REC_LEN(de) ||
+ (de->rec_len + offset == EXT2_BLOCK_SIZE(ctx->fs->super))) {
+ if (ext2_get_dirent_dirdata_size(de, EXT2_DIRENT_LUFID) ==
+ EXT2_DIRENT_LUFID_SIZE)
+ return 0;
+ }
+ /* just clear dirent data flags for now, we should fix FID data
+ * in lustre specific pass.
+ */
+ if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) {
+ ext2_fix_dirent_dirdata(de);
+ if (ext2_get_dirent_dirdata_size(de, EXT2_DIRENT_LUFID) !=
+ EXT2_DIRENT_LUFID_SIZE) {
+ de->file_type &= ~EXT2_DIRENT_LUFID;
+ }
+
+ return 2;
+ }
+ }
+ return 1;
+}
/*
* Make sure the first entry in the directory is '.', and that the
* directory entry is sane.
*/
static int check_dot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *dirent, unsigned int offset,
ext2_ino_t ino, struct problem_context *pctx)
{
struct ext2_dir_entry *nextdir;
int status = 0;
int created = 0;
int problem = 0;
+ int dir_data_error;
if (!dirent->inode)
problem = PR_2_MISSING_DOT;
else if (dirent->name[1] != '\0')
problem = PR_2_DOT_NULL_TERM;
+ dir_data_error = e2fsck_check_dirent_data(ctx,
+ (struct ext2_dir_entry_2 *)dirent,
+ offset, pctx);
+
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
if (problem) {
if (fix_problem(ctx, problem, pctx)) {
- if (rec_len < 12)
+ if (rec_len < 12 && dir_data_error)
rec_len = dirent->rec_len = 12;
dirent->inode = ino;
dirent->name_len = 1;
status = 1;
}
}
- if (rec_len > 12) {
+ if (rec_len > 12 && dir_data_error) {
new_len = rec_len - 12;
if (new_len > 12) {
if (created ||
* here; this gets done in pass 3.
*/
static int check_dotdot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *dirent, unsigned int offset,
ext2_ino_t ino, struct problem_context *pctx)
{
int problem = 0;
unsigned int rec_len;
+ int dir_data_error;
if (!dirent->inode)
problem = PR_2_MISSING_DOT_DOT;
else if (dirent->name[2] != '\0')
problem = PR_2_DOT_DOT_NULL_TERM;
+ dir_data_error = e2fsck_check_dirent_data(ctx,
+ (struct ext2_dir_entry_2 *)dirent,
+ offset, pctx);
+
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
if (problem) {
if (fix_problem(ctx, problem, pctx)) {
- if (rec_len < 12)
+ if (rec_len < 12 && dir_data_error)
dirent->rec_len = 12;
/*
* Note: we don't have the parent inode just
int should_be = EXT2_FT_UNKNOWN;
__u16 result;
struct ext2_inode inode;
+ __u8 dirdata = 0;
+
+ if (ctx->fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) {
+ dirdata = filetype & ~EXT2_FT_MASK;
+ filetype = filetype & EXT2_FT_MASK;
+ }
if (!(ctx->fs->super->s_feature_incompat &
EXT2_FEATURE_INCOMPAT_FILETYPE)) {
pctx) == 0)
return 0;
- dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
+ dirent->name_len = ((dirent->name_len & 0xFF) | (dirdata | should_be) << 8);
return 1;
}
ext2_dirhash_t hash = 0, prev_hash;
if (db->blockcnt == 0) {
- root = (struct ext2_dx_root_info *) (block_buf + 24);
+ root = get_ext2_dx_root_info(fs, block_buf);
#ifdef DX_DEBUG
printf("Root node dump:\n");
printf("\t Flags: %d\n", root->unused_flags);
#endif
- ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+ ent = (struct ext2_dx_entry *) ((char *) root + root->info_length);
} else {
ent = (struct ext2_dx_entry *) (block_buf+8);
}
(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);
+ root = get_ext2_dx_root_info(fs, buf);
dx_db->type = DX_DIRBLOCK_ROOT;
dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
if ((root->reserved_zero ||
}
if (dot_state == 0) {
- if (check_dot(ctx, dirent, ino, &cd->pctx))
+ if (check_dot(ctx, dirent, offset, ino, &cd->pctx))
dir_modified++;
} else if (dot_state == 1) {
- ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
+ ret = check_dotdot(ctx, dirent, offset, ino, &cd->pctx);
if (ret < 0)
goto abort_free_dict;
if (ret)
if (!dirent->inode)
goto next;
+ ret = e2fsck_check_dirent_data(ctx, (struct ext2_dir_entry_2 *)dirent,
+ offset, &cd->pctx);
+ if (ret == 2)
+ dir_modified++;
+
/*
* Make sure the inode listed is a legal one.
*/
struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
errcode_t retval;
struct problem_context pctx;
+ __u16 dirdata = 0;
if ((dirent->name_len & 0xFF) != 2)
return 0;
fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
}
dirent->inode = fp->parent;
+
+ dirdata = dirent->name_len & (~EXT2_FT_MASK << 8);
+
if (fp->ctx->fs->super->s_feature_incompat &
EXT2_FEATURE_INCOMPAT_FILETYPE)
dirent->name_len = (dirent->name_len & 0xFF) |
else
dirent->name_len = dirent->name_len & 0xFF;
+ if (fp->ctx->fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA)
+ dirent->name_len |= dirdata;
+
fp->done++;
return DIRENT_ABORT | DIRENT_CHANGED;
}
N_("@i %i is badly corrupt (badness value = %N). "),
PROMPT_CLEAR, PR_PREEN_OK },
+ /* Directory entry dirdata length set incorrectly */
+ { PR_2_CLEAR_DIRDATA,
+ N_("@E dirdata length set incorrectly.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
/* Pass 3 errors */
/* Pass 3: Checking directory connectivity */
/* Inode completely corrupt */
#define PR_2_INODE_TOOBAD 0x020049
+/* Directory dirdata flag set */
+#define PR_2_CLEAR_DIRDATA 0x02f000
+
/*
* Pass 3 errors
*/
}
ent = fd->harray + fd->num_array++;
ent->dir = dirent;
- fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+ fd->dir_size +=
+ EXT2_DIR_REC_LEN((struct ext2_dir_entry_2 *)dirent);
ent->ino = dirent->inode;
if (fd->compress)
ent->hash = ent->minor_hash = 0;
ent = fd->harray + i;
if (ent->dir->inode == 0)
continue;
- rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
+ rec_len = EXT2_DIR_REC_LEN((struct ext2_dir_entry_2 *)ent->dir);
if (rec_len > left) {
if (left) {
left += prev_rec_len;
if (retval)
return retval;
prev_rec_len = rec_len;
- memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
+ memcpy(dirent->name, ent->dir->name, rec_len);
offset += rec_len;
left -= rec_len;
if (left < slack) {
return retval;
}
+/*
+ * Compute the total directory entry data length.
+ * This includes the filename and an implicit NUL terminator (always present),
+ * and optional extensions. Each extension has a bit set in the high 4 bits of
+ * de->file_type, and the extension length is the first byte in each entry.
+ */
+int ext2_get_dirent_dirdata_size(struct ext2_dir_entry_2 *de,
+ char dirdata_flags)
+{
+ char *len = de->name + de->name_len + 1 /* NUL terminator */;
+ int dlen = 0;
+ __u8 extra_data_flags = (de->file_type & ~EXT2_FT_MASK) >> 4;
+
+ dirdata_flags >>= 4;
+ while ((extra_data_flags & dirdata_flags) != 0) {
+ if (extra_data_flags & 1) {
+ if (dirdata_flags & 1)
+ dlen += *len;
+
+ len += *len;
+ }
+ extra_data_flags >>= 1;
+ dirdata_flags >>= 1;
+ }
+
+ /* add NUL terminator byte to dirdata length */
+ return dlen + (dlen != 0);
+}
+
+int ext2_get_dirent_size(struct ext2_dir_entry_2 *de)
+{
+ return ext2_get_dirent_dirdata_size(de, ~EXT2_FT_MASK);
+}
+
errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
void *buf, int flags EXT2FS_ATTR((unused)))
{
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
EXT4_FEATURE_INCOMPAT_MMP| \
- EXT4_FEATURE_INCOMPAT_EA_INODE)
+ EXT4_FEATURE_INCOMPAT_EA_INODE| \
+ EXT4_FEATURE_INCOMPAT_DIRDATA)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
#define EXT2_FT_SYMLINK 7
#define EXT2_FT_MAX 8
+#define EXT2_FT_MASK 0x0f
/*
* EXT2_DIR_PAD defines the directory entries boundaries
*/
#define EXT2_DIR_PAD 4
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
-#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+#define __EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
+#define EXT2_DIR_REC_LEN(de) (__EXT2_DIR_REC_LEN((de)->name_len + \
+ ext2_get_dirent_size(de)))
+/* lu_fid size and NUL char */
+#define EXT2_DIRENT_LUFID_SIZE (17 + 1)
+#define EXT2_DIRENT_LUFID 0x10
+
/*
* This structure is used for multiple mount protection. It is written
* into the block number saved in the s_mmp_block field in the superblock.
*/
#define EXT4_MMP_MIN_CHECK_INTERVAL 5
+int ext2_get_dirent_dirdata_size(struct ext2_dir_entry_2 *de, char dirdata_flags);
+int ext2_get_dirent_size(struct ext2_dir_entry_2 *de);
+
#endif /* _LINUX_EXT2_FS_H */
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
EXT4_FEATURE_INCOMPAT_EA_INODE|\
+ EXT4_FEATURE_INCOMPAT_DIRDATA|\
EXT4_FEATURE_INCOMPAT_64BIT)
#else
#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_FEATURE_INCOMPAT_MMP|\
EXT4_FEATURE_INCOMPAT_EA_INODE|\
+ EXT4_FEATURE_INCOMPAT_DIRDATA|\
EXT4_FEATURE_INCOMPAT_64BIT)
#endif
#ifdef CONFIG_QUOTA
return ext2fs_inode_data_blocks2(fs, inode);
}
+_INLINE_ struct ext2_dx_root_info *get_ext2_dx_root_info(ext2_filsys fs,
+ char *buf)
+{
+ struct ext2_dir_entry_2 *de = (struct ext2_dir_entry_2 *)buf;
+
+ if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA))
+ return (struct ext2_dx_root_info *)(buf +
+ __EXT2_DIR_REC_LEN(1) +
+ __EXT2_DIR_REC_LEN(2));
+
+ /* get dotdot first */
+ de = (struct ext2_dir_entry_2 *)((char *)de + de->rec_len);
+
+ /* dx root info is after dotdot entry */
+ de = (struct ext2_dir_entry_2 *)((char *)de + EXT2_DIR_REC_LEN(de));
+
+ return (struct ext2_dx_root_info *)de;
+}
+
/*
* This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b)
*/
unsigned int rec_len, min_rec_len, curr_rec_len;
int ret = 0;
- rec_len = EXT2_DIR_REC_LEN(ls->namelen);
+ rec_len = __EXT2_DIR_REC_LEN(ls->namelen);
ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
if (ls->err)
* truncate it and return.
*/
if (dirent->inode) {
- min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+ min_rec_len = __EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
if (curr_rec_len < (min_rec_len + rec_len))
return ret;
rec_len = curr_rec_len - min_rec_len;
dir->inode = dir_ino;
dir->name_len = 1 | filetype;
dir->name[0] = '.';
- rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
- dir->rec_len = EXT2_DIR_REC_LEN(1);
+ rec_len = fs->blocksize - __EXT2_DIR_REC_LEN(1);
+ dir->rec_len = __EXT2_DIR_REC_LEN(1);
/*
* Set up entry for '..'
EXT4_FEATURE_INCOMPAT_FLEX_BG|
EXT4_FEATURE_INCOMPAT_EA_INODE|
EXT4_FEATURE_INCOMPAT_MMP |
+ EXT4_FEATURE_INCOMPAT_DIRDATA|
EXT4_FEATURE_INCOMPAT_64BIT,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
EXT3_FEATURE_INCOMPAT_EXTENTS |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
EXT4_FEATURE_INCOMPAT_EA_INODE|
- EXT4_FEATURE_INCOMPAT_MMP,
+ EXT4_FEATURE_INCOMPAT_MMP|
+ EXT4_FEATURE_INCOMPAT_DIRDATA,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
EXT2_FEATURE_INCOMPAT_FILETYPE |
EXT4_FEATURE_INCOMPAT_FLEX_BG |
EXT4_FEATURE_INCOMPAT_EA_INODE|
- EXT4_FEATURE_INCOMPAT_MMP,
+ EXT4_FEATURE_INCOMPAT_MMP|
+ EXT4_FEATURE_INCOMPAT_DIRDATA,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|