Message ID | 20180504070923.45140-2-c17828@cray.com |
---|---|
State | Changes Requested |
Headers | show |
Series | 64bit inode e2fsprogs support | expand |
Hello, Just noticed I introduced error while was fixing warnings from checkpatch.pl This fix is need for f_dir_optimize to be passed. diff --git a/debugfs/ls.c b/debugfs/ls.c index 082c14b9..5668d018 100644 --- a/debugfs/ls.c +++ b/debugfs/ls.c @@ -27,8 +27,8 @@ extern char *optarg; #ifndef DFID #define DFID "[%#llx:0x%x:0x%x]" -#define PFID(fid) ((unsigned long long)fid_seq(fid),\ - fid_oid(fid), fid_ver(fid)) +#define PFID(fid) (unsigned long long)fid_seq(fid),\ + fid_oid(fid), fid_ver(fid) struct lu_fid { __u64 f_seq; __u32 f_oid; Best regards, > On 4 May 2018, at 10:09, c17828 <artem.blagodarenko@gmail.com> wrote: > > From: Andreas Dilger <andreas.dilger@intel.com> > > Add support for the INCOMPAT_DIRDATA feature, which allows > storing extra data in the directory entry beyond the name. > This allows the Lustre File IDentifier to be accessed in > an efficient manner, and would be useful for expanding a > filesystem to allow more than 2^32 inodes in the future. > > Include this patches: > > e2fsck: e2fsck -D does not change dirdata content > > Fix dir optimization to preserve dirdata content for dot > and dotdot entries. > > Lustre-bug: https://jira.hpdd.intel.com/browse/LU-1774 > Signed-off-by: Bobi Jam <bobijam.xu@intel.com> > Change-Id: Iae190794da75a2080a8e5cc5b95a49e0c894f72f > > e2fsprogs: Consider DIRENT_LUFID flag in link_proc(). > > While adding the new file entry in directory block, link_proc() > calculates minimum record length of the existing directory entry > without considering the dirent data size and which leads to > corruption. Changed the code to use EXT2_DIR_REC_LEN() which will > return correct record length including dirent data size. > > Lustre-bug: https://jira.hpdd.intel.com/browse/LU-2462 > Signed-off-by: Manisha Salve <msalve@ddn.com> > Change-Id: Ic593c558c47a78183143ec8e99d8385ac94d06f7 > > libext2fs, e2fsck: don't use ext2_dir_entry_2 > > Due to endian issues, do not use ext2_dir_entry_2 because it will > have the wrong byte order on directory entries that are swabbed. > Instead, use the standard practice of mask-and-shift to access the > file_type and dirdata flags. > > Lustre-bug: https://jira.hpdd.intel.com/browse/LU-4677 > Signed-off-by: Pravin Shelar <pravin@clusterfs.com> > Signed-off-by: Andreas Dilger <andreas.dilger@intel.com> > Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com> > --- > debugfs/htree.c | 2 +- > debugfs/ls.c | 67 ++++++++++++++++++++++- > e2fsck/pass1.c | 4 +- > e2fsck/pass2.c | 139 ++++++++++++++++++++++++++++++++++++++--------- > e2fsck/pass3.c | 7 +++ > e2fsck/problem.c | 5 ++ > e2fsck/problem.h | 3 + > e2fsck/rehash.c | 82 ++++++++++++++++------------ > lib/ext2fs/dirblock.c | 33 +++++++++++ > lib/ext2fs/ext2_fs.h | 18 +++++- > lib/ext2fs/ext2fs.h | 22 +++++++- > lib/ext2fs/inline_data.c | 14 ++--- > lib/ext2fs/link.c | 10 ++-- > lib/ext2fs/newdir.c | 4 +- > misc/mke2fs.c | 8 +++ > misc/tune2fs.c | 2 + > 16 files changed, 338 insertions(+), 82 deletions(-) > > diff --git a/debugfs/htree.c b/debugfs/htree.c > index cf7d78aa..b7f1add0 100644 > --- a/debugfs/htree.c > +++ b/debugfs/htree.c > @@ -278,7 +278,7 @@ void do_htree_dump(int argc, char *argv[]) > goto errout; > } > > - rootnode = (struct ext2_dx_root_info *) (buf + 24); > + rootnode = get_ext2_dx_root_info(current_fs, buf); > > fprintf(pager, "Root node dump:\n"); > fprintf(pager, "\t Reserved zero: %u\n", rootnode->reserved_zero); > diff --git a/debugfs/ls.c b/debugfs/ls.c > index 61b63196..082c14b9 100644 > --- a/debugfs/ls.c > +++ b/debugfs/ls.c > @@ -25,6 +25,30 @@ extern char *optarg; > > #include "debugfs.h" > > +#ifndef DFID > +#define DFID "[%#llx:0x%x:0x%x]" > +#define PFID(fid) ((unsigned long long)fid_seq(fid),\ > + fid_oid(fid), fid_ver(fid)) > +struct lu_fid { > + __u64 f_seq; > + __u32 f_oid; > + __u32 f_ver; > +}; > +#endif /* !DFID */ > + > +#ifndef HAVE_LUSTRE_LUSTRE_IDL_H > +#define fid_seq(fid) ((fid)->f_seq) > +#define fid_oid(fid) ((fid)->f_oid) > +#define fid_ver(fid) ((fid)->f_ver) > + > +static inline void fid_be_to_cpu(struct lu_fid *dst, struct lu_fid *src) > +{ > + dst->f_seq = ext2fs_be64_to_cpu(src->f_seq); > + dst->f_oid = ext2fs_be32_to_cpu(src->f_oid); > + dst->f_ver = ext2fs_be32_to_cpu(src->f_ver); > +} > +#endif > + > /* > * list directory > */ > @@ -32,6 +56,7 @@ extern char *optarg; > #define LONG_OPT 0x0001 > #define PARSE_OPT 0x0002 > #define RAW_OPT 0x0004 > +#define DIRDATA_OPT 0x0008 > #define ENCRYPT_OPT 0x8000 > > struct list_dir_struct { > @@ -44,6 +69,41 @@ struct list_dir_struct { > static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", > "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; > > +static void list_dirdata(struct list_dir_struct *ls, > + struct ext2_dir_entry *dirent) > +{ > + unsigned char *data; > + int dlen; > + __u8 dirdata_mask; > + __u8 file_type = dirent->name_len >> 8; > + > + data = (unsigned char *)dirent->name + > + (dirent->name_len & EXT2_NAME_LEN) + 1; > + > + for (dirdata_mask = EXT2_FT_MASK + 1; > + dirdata_mask != 0; dirdata_mask <<= 1) { > + if ((dirdata_mask & file_type) == 0) > + continue; > + > + dlen = data[0]; > + > + if (dirdata_mask == EXT2_DIRENT_LUFID) { > + struct lu_fid *fid = (struct lu_fid *)(data + 1); > + > + fid_be_to_cpu(fid, fid); > + fprintf(ls->f, "fid:"DFID, PFID(fid)); > + } else { > + int i; > + > + for (i = 1; i < dlen; i++) > + fprintf(ls->f, "%02x", data[i]); > + } > + > + fprintf(ls->f, " "); > + data += dlen; > + } > +} > + > static int print_filename(FILE *f, struct ext2_dir_entry *dirent, int options) > { > unsigned char ch; > @@ -157,6 +217,8 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), > else > fprintf(ls->f, "%5llu", EXT2_I_SIZE(&inode)); > fprintf(ls->f, " %s ", datestr); > + if ((ls->options & DIRDATA_OPT) != 0) > + list_dirdata(ls, dirent); > print_filename(ls->f, dirent, options); > fputc('\n', ls->f); > } else { > @@ -204,7 +266,7 @@ void do_list_dir(int argc, char *argv[]) > return; > > reset_getopt(); > - while ((c = getopt (argc, argv, "cdlpr")) != EOF) { > + while ((c = getopt(argc, argv, "cdDlpr")) != EOF) { > switch (c) { > case 'c': > flags |= DIRENT_FLAG_INCLUDE_CSUM; > @@ -212,6 +274,9 @@ void do_list_dir(int argc, char *argv[]) > case 'l': > ls.options |= LONG_OPT; > break; > + case 'D': > + ls.options |= DIRDATA_OPT; > + break; > case 'd': > flags |= DIRENT_FLAG_INCLUDE_REMOVED; > break; > diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c > index 5015d938..d39086d2 100644 > --- a/e2fsck/pass1.c > +++ b/e2fsck/pass1.c > @@ -719,7 +719,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, > */ > memcpy(&dotdot, inode->i_block, sizeof(dotdot)); > memcpy(&de, ((char *)inode->i_block) + EXT4_INLINE_DATA_DOTDOT_SIZE, > - EXT2_DIR_REC_LEN(0)); > + EXT2_DIR_NAME_LEN(0)); > dotdot = ext2fs_le32_to_cpu(dotdot); > de.inode = ext2fs_le32_to_cpu(de.inode); > de.rec_len = ext2fs_le16_to_cpu(de.rec_len); > @@ -2646,7 +2646,7 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, > return 1; > > /* 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)) > diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c > index 1b0504c8..c419a016 100644 > --- a/e2fsck/pass2.c > +++ b/e2fsck/pass2.c > @@ -366,13 +366,88 @@ static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) > return (int) (db_a->blockcnt - db_b->blockcnt); > } > > +void ext2_fix_dirent_dirdata(struct ext2_dir_entry *de) > +{ > + __u16 file_type = de->name_len & (EXT2_FT_MASK << 8); > + __u8 de_flags = (de->name_len >> 8) & ~EXT2_FT_MASK; > + __u8 name_len = de->name_len & EXT2_NAME_LEN; > + __u8 new_flag = 0; > + int i; > + > + for (i = 0; i < 4; i++) { > + __u8 flags = new_flag | (1 << i) << 4; > + > + /* new_flag is accumulating flags that are set in de_flags > + * and still fit inside rec_len. ext2_get_dirent_dirdata_size() > + * returns the size of all the dirdata entries in flags, and > + * chops off any that are beyond rec_len. > + */ > + if ((de_flags & flags) == flags) { > + int dirdatalen = ext2_get_dirdata_field_size(de, > + flags); > + int rlen = EXT2_DIR_NAME_LEN(name_len + dirdatalen); > + > + if (rlen > de->rec_len) > + break; > + > + new_flag |= flags; > + } > + } > + > + de->name_len = name_len | file_type | (new_flag << 8); > +} > + > +/* > + * 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 *de, > + unsigned int offset, struct problem_context *pctx) > +{ > + if (!(ctx->fs->super->s_feature_incompat & > + EXT4_FEATURE_INCOMPAT_DIRDATA)) { > + if ((de->name_len >> 8) & ~EXT2_FT_MASK) { > + /* clear dirent extra data flags. */ > + if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) { > + de->name_len &= (EXT2_FT_MASK << 8) | > + EXT2_NAME_LEN; > + return 2; > + } > + } > + return 1; > + } > + if ((de->name_len >> 8) & ~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_dirdata_field_size(de, > + EXT2_DIRENT_LUFID) % > + EXT2_DIRENT_LUFID_SIZE == 1 /*size*/ + 1 /*NULL*/) > + 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_dirdata_field_size(de, > + EXT2_DIRENT_LUFID) != > + EXT2_DIRENT_LUFID_SIZE) > + de->name_len &= ~(EXT2_DIRENT_LUFID << 8); > + > + 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; > @@ -380,6 +455,7 @@ static int check_dot(e2fsck_t ctx, > int status = 0; > int created = 0; > problem_t problem = 0; > + int dir_data_error; > > if (!dirent->inode) > problem = PR_2_MISSING_DOT; > @@ -389,10 +465,12 @@ static int check_dot(e2fsck_t ctx, > else if (dirent->name[1] != '\0') > problem = PR_2_DOT_NULL_TERM; > > + dir_data_error = e2fsck_check_dirent_data(ctx, 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; > ext2fs_dirent_set_name_len(dirent, 1); > @@ -411,7 +489,7 @@ static int check_dot(e2fsck_t ctx, > } > if (rec_len > 12) { > new_len = rec_len - 12; > - if (new_len > 12) { > + if (new_len > 12 && dir_data_error) { > if (created || > fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { > nextdir = (struct ext2_dir_entry *) > @@ -436,11 +514,12 @@ static int check_dot(e2fsck_t ctx, > * 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) > { > problem_t problem = 0; > unsigned int rec_len; > + int dir_data_error; > > if (!dirent->inode) > problem = PR_2_MISSING_DOT_DOT; > @@ -451,10 +530,12 @@ static int check_dotdot(e2fsck_t ctx, > else if (dirent->name[2] != '\0') > problem = PR_2_DOT_DOT_NULL_TERM; > > + dir_data_error = e2fsck_check_dirent_data(ctx, 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 > @@ -528,6 +609,12 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, > int filetype = ext2fs_dirent_file_type(dirent); > int should_be = EXT2_FT_UNKNOWN; > struct ext2_inode inode; > + __u8 dirdata = 0; > + > + if (ext2fs_has_feature_dirdata(ctx->fs->super)) { > + dirdata = filetype & ~EXT2_FT_MASK; > + filetype = filetype & EXT2_FT_MASK; > + } > > if (!ext2fs_has_feature_filetype(ctx->fs->super)) { > if (filetype == 0 || > @@ -558,8 +645,7 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, > if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, > pctx) == 0) > return 0; > - > - ext2fs_dirent_set_file_type(dirent, should_be); > + ext2fs_dirent_set_file_type(dirent, should_be | dirdata); > return 1; > } > > @@ -581,7 +667,7 @@ static void parse_int_node(ext2_filsys fs, > int csum_size = 0; > > 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"); > @@ -591,8 +677,8 @@ static void parse_int_node(ext2_filsys fs, > printf("\t Indirect levels: %d\n", root->indirect_levels); > 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); > > if (failed_csum && > (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || > @@ -600,7 +686,7 @@ static void parse_int_node(ext2_filsys fs, > &cd->pctx))) > goto clear_and_exit; > } else { > - ent = (struct ext2_dx_entry *) (block_buf+8); > + ent = (struct ext2_dx_entry *)(block_buf + 8); > > if (failed_csum && > (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || > @@ -608,8 +694,7 @@ static void parse_int_node(ext2_filsys fs, > &cd->pctx))) > goto clear_and_exit; > } > - > - limit = (struct ext2_dx_countlimit *) ent; > + limit = (struct ext2_dx_countlimit *)ent; > > #ifdef DX_DEBUG > printf("Number of entries (count): %d\n", > @@ -780,7 +865,6 @@ static void salvage_directory(ext2_filsys fs, > } > } > > -#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) > static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) > { > struct ext2_dir_entry *d; > @@ -790,11 +874,11 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) > d = dirbuf; > top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize); > > - while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top) > - d = NEXT_DIRENT(d); > + while (d->rec_len && !(d->rec_len & 0x3) && EXT2_NEXT_DIRENT(d) <= top) > + d = EXT2_NEXT_DIRENT(d); > > if (d != top) { > - unsigned int min_size = EXT2_DIR_REC_LEN( > + unsigned int min_size = EXT2_DIR_NAME_LEN( > ext2fs_dirent_name_len(dirbuf)); > if (min_size > (char *)top - (char *)d) > return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; > @@ -809,7 +893,6 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) > > return 0; > } > -#undef NEXT_DIRENT > > static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, > size_t *inline_data_size, > @@ -828,7 +911,7 @@ static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, > */ > if (old_size > EXT4_MIN_INLINE_DATA_SIZE && > old_size < EXT4_MIN_INLINE_DATA_SIZE + > - EXT2_DIR_REC_LEN(1)) { > + EXT2_DIR_NAME_LEN(1)) { > old_size = EXT4_MIN_INLINE_DATA_SIZE; > new_size = old_size; > } else > @@ -1035,7 +1118,7 @@ inline_read_fail: > if (((inline_data_size & 3) || > (inline_data_size > EXT4_MIN_INLINE_DATA_SIZE && > inline_data_size < EXT4_MIN_INLINE_DATA_SIZE + > - EXT2_DIR_REC_LEN(1))) && > + EXT2_DIR_NAME_LEN(1))) && > fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) { > errcode_t err = fix_inline_dir_size(ctx, ino, > &inline_data_size, &pctx, > @@ -1085,7 +1168,7 @@ inline_read_fail: > (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 || > @@ -1165,7 +1248,7 @@ skip_checksum: > * force salvaging this dir. > */ > if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN) > - rec_len = EXT2_DIR_REC_LEN(1); > + rec_len = EXT2_DIR_NAME_LEN(1); > else > (void) ext2fs_get_rec_len(fs, dirent, &rec_len); > cd->pctx.dirent = dirent; > @@ -1227,7 +1310,7 @@ skip_checksum: > memset(&dot, 0, sizeof(dot)); > dirent = ˙ > dirent->inode = ino; > - dirent->rec_len = EXT2_DIR_REC_LEN(1); > + dirent->rec_len = EXT2_DIR_NAME_LEN(1); > dirent->name_len = 1 | filetype; > dirent->name[0] = '.'; > } else if (dot_state == 1) { > @@ -1235,7 +1318,7 @@ skip_checksum: > dirent = &dotdot; > dirent->inode = > ((struct ext2_dir_entry *)buf)->inode; > - dirent->rec_len = EXT2_DIR_REC_LEN(2); > + dirent->rec_len = EXT2_DIR_NAME_LEN(2); > dirent->name_len = 2 | filetype; > dirent->name[0] = '.'; > dirent->name[1] = '.'; > @@ -1247,10 +1330,10 @@ skip_checksum: > } > > 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) > @@ -1266,6 +1349,10 @@ skip_checksum: > if (!dirent->inode) > goto next; > > + ret = e2fsck_check_dirent_data(ctx, dirent, offset, &cd->pctx); > + if (ret == 2) > + dir_modified++; > + > /* > * Make sure the inode listed is a legal one. > */ > diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c > index 6a975b36..4a777213 100644 > --- a/e2fsck/pass3.c > +++ b/e2fsck/pass3.c > @@ -698,6 +698,7 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent, > struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data; > errcode_t retval; > struct problem_context pctx; > + __u16 dirdata = 0; > > if (ext2fs_dirent_name_len(dirent) != 2) > return 0; > @@ -717,11 +718,17 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent, > fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); > } > dirent->inode = fp->parent; > + > + dirdata = dirent->name_len & (~EXT2_FT_MASK << 8); > + > if (ext2fs_has_feature_filetype(fp->ctx->fs->super)) > ext2fs_dirent_set_file_type(dirent, EXT2_FT_DIR); > else > ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); > > + if (ext2fs_has_feature_dirdata(fp->ctx->fs->super)) > + dirent->name_len |= dirdata; > + > fp->done++; > return DIRENT_ABORT | DIRENT_CHANGED; > } > diff --git a/e2fsck/problem.c b/e2fsck/problem.c > index edc9d51f..2a86d528 100644 > --- a/e2fsck/problem.c > +++ b/e2fsck/problem.c > @@ -1671,6 +1671,11 @@ static struct e2fsck_problem problem_table[] = { > N_("Encrypted @E is too short.\n"), > PROMPT_CLEAR, 0 }, > > + /* 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 */ > diff --git a/e2fsck/problem.h b/e2fsck/problem.h > index 482d111a..05214840 100644 > --- a/e2fsck/problem.h > +++ b/e2fsck/problem.h > @@ -1004,6 +1004,9 @@ struct problem_context { > /* Encrypted directory entry is too short */ > #define PR_2_BAD_ENCRYPTED_NAME 0x020050 > > +/* Entry dirdata length set incorrectly */ > +#define PR_2_CLEAR_DIRDATA 0x020051 > + > /* > * Pass 3 errors > */ > diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c > index 486e1f21..546b073c 100644 > --- a/e2fsck/rehash.c > +++ b/e2fsck/rehash.c > @@ -85,6 +85,8 @@ struct fill_dir_struct { > int compress; > ino_t parent; > ext2_ino_t dir; > + struct ext2_dir_entry *dot_de; > + struct ext2_dir_entry *dotdot_de; > }; > > struct hash_entry { > @@ -160,11 +162,14 @@ static int fill_dir_block(ext2_filsys fs, > if (dirent->inode == 0) > continue; > if (!fd->compress && (name_len == 1) && > - (dirent->name[0] == '.')) > + (dirent->name[0] == '.')) { > + fd->dot_de = dirent; > continue; > + } > if (!fd->compress && (name_len == 2) && > (dirent->name[0] == '.') && (dirent->name[1] == '.')) { > fd->parent = dirent->inode; > + fd->dotdot_de = dirent; > continue; > } > if (fd->num_array >= fd->max_array) { > @@ -179,7 +184,7 @@ static int fill_dir_block(ext2_filsys fs, > } > ent = fd->harray + fd->num_array++; > ent->dir = dirent; > - fd->dir_size += EXT2_DIR_REC_LEN(name_len); > + fd->dir_size += EXT2_DIR_REC_LEN(dirent); > ent->ino = dirent->inode; > if (fd->compress) > ent->hash = ent->minor_hash = 0; > @@ -475,7 +480,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, > ent = fd->harray + i; > if (ent->dir->inode == 0) > continue; > - rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir)); > + rec_len = EXT2_DIR_REC_LEN(ent->dir); > if (rec_len > left) { > if (left) { > left += prev_rec_len; > @@ -510,8 +515,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, > if (retval) > return retval; > prev_rec_len = rec_len; > - memcpy(dirent->name, ent->dir->name, > - ext2fs_dirent_name_len(dirent)); > + memcpy(dirent->name, ent->dir->name, rec_len); > offset += rec_len; > left -= rec_len; > if (left < slack) { > @@ -536,44 +540,49 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, > > > static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, > - ext2_ino_t ino, ext2_ino_t parent) > + ext2_ino_t ino, ext2_ino_t parent, > + struct ext2_dir_entry *dot_de, > + struct ext2_dir_entry *dotdot_de) > { > - struct ext2_dir_entry *dir; > - struct ext2_dx_root_info *root; > + struct ext2_dir_entry *dirent; > + struct ext2_dx_root_info *root; > struct ext2_dx_countlimit *limits; > - int filetype = 0; > int csum_size = 0; > - > - if (ext2fs_has_feature_filetype(fs->super)) > - filetype = EXT2_FT_DIR; > + int offset; > + int rec_len; > > memset(buf, 0, fs->blocksize); > - dir = (struct ext2_dir_entry *) buf; > - dir->inode = ino; > - dir->name[0] = '.'; > - ext2fs_dirent_set_name_len(dir, 1); > - ext2fs_dirent_set_file_type(dir, filetype); > - dir->rec_len = 12; > - dir = (struct ext2_dir_entry *) (buf + 12); > - dir->inode = parent; > - dir->name[0] = '.'; > - dir->name[1] = '.'; > - ext2fs_dirent_set_name_len(dir, 2); > - ext2fs_dirent_set_file_type(dir, filetype); > - dir->rec_len = fs->blocksize - 12; > - > - root = (struct ext2_dx_root_info *) (buf+24); > + dirent = (struct ext2_dir_entry *) buf; > + dirent->inode = ino; > + > + dirent->name_len = dot_de->name_len; > + offset = rec_len = dirent->rec_len = dot_de->rec_len; > + memcpy(dirent->name, dot_de->name, rec_len); > + > + dirent = EXT2_NEXT_DIRENT(dirent); > + /* set to jump over the index block */ > + > + dirent->inode = parent; > + > + dirent->name_len = dotdot_de->name_len; > + dirent->rec_len = fs->blocksize - rec_len; > + rec_len = EXT2_DIR_REC_LEN(dotdot_de); > + memcpy(dirent->name, dotdot_de->name, rec_len); > + offset += rec_len; > + > + root = (struct ext2_dx_root_info *)(buf + offset); > root->reserved_zero = 0; > root->hash_version = fs->super->s_def_hash_version; > - root->info_length = 8; > + root->info_length = sizeof(*root); > root->indirect_levels = 0; > root->unused_flags = 0; > + offset += root->info_length; > > if (ext2fs_has_feature_metadata_csum(fs->super)) > csum_size = sizeof(struct ext2_dx_tail); > > - limits = (struct ext2_dx_countlimit *) (buf+32); > - limits->limit = (fs->blocksize - (32 + csum_size)) / > + limits = (struct ext2_dx_countlimit *) (buf + offset); > + limits->limit = (fs->blocksize - (offset + csum_size)) / > sizeof(struct ext2_dx_entry); > limits->count = 0; > > @@ -647,7 +656,9 @@ static int alloc_blocks(ext2_filsys fs, > static errcode_t calculate_tree(ext2_filsys fs, > struct out_dir *outdir, > ext2_ino_t ino, > - ext2_ino_t parent) > + ext2_ino_t parent, > + struct ext2_dir_entry *dot_de, > + struct ext2_dir_entry *dotdot_de) > { > struct ext2_dx_root_info *root_info; > struct ext2_dx_entry *root, *int_ent, *dx_ent = 0; > @@ -657,7 +668,9 @@ static errcode_t calculate_tree(ext2_filsys fs, > int i, c1, c2, c3, nblks; > int limit_offset, int_offset, root_offset; > > - root_info = set_root_node(fs, outdir->buf, ino, parent); > + root_info = set_root_node(fs, outdir->buf, ino, parent, dot_de, > + dotdot_de); > + > root_offset = limit_offset = ((char *) root_info - outdir->buf) + > root_info->info_length; > root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); > @@ -944,11 +957,10 @@ resort: > if (retval) > goto errout; > > - free(dir_buf); dir_buf = 0; > - > if (!fd.compress) { > /* Calculate the interior nodes */ > - retval = calculate_tree(fs, &outdir, ino, fd.parent); > + retval = calculate_tree(fs, &outdir, ino, fd.parent, > + fd.dot_de, fd.dotdot_de); > if (retval) > goto errout; > } > diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c > index 54b27772..3563138d 100644 > --- a/lib/ext2fs/dirblock.c > +++ b/lib/ext2fs/dirblock.c > @@ -50,6 +50,39 @@ errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, > return ext2fs_read_dir_block4(fs, block, buf, flags, 0); > } > > +/* > + * Compute the dirdata length. This includes only 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_dirdata_field_size(struct ext2_dir_entry *de, > + char dirdata_flags) > +{ > + char *lenp = de->name + (de->name_len & EXT2_NAME_LEN) + 1 /* NUL */; > + __u8 extra_data_flags = (de->name_len & ~(EXT2_FT_MASK << 8)) >> 12; > + int dlen = 0; > + > + dirdata_flags >>= 4; > + while ((extra_data_flags & dirdata_flags) != 0) { > + if (extra_data_flags & 1) { > + if (dirdata_flags & 1) > + dlen += *lenp; > + > + lenp += *lenp; > + } > + extra_data_flags >>= 1; > + dirdata_flags >>= 1; > + } > + > + /* add NUL terminator byte to dirdata length */ > + return dlen + (dlen != 0); > +} > + > +int ext2_get_dirdata_size(struct ext2_dir_entry *de) > +{ > + return ext2_get_dirdata_field_size(de, ~EXT2_FT_MASK); > +} > + > errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, > void *buf, int flags EXT2FS_ATTR((unused))) > { > diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h > index 2496d16d..4919f946 100644 > --- a/lib/ext2fs/ext2_fs.h > +++ b/lib/ext2fs/ext2_fs.h > @@ -923,7 +923,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, 4, ENCRYPT) > #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ > EXT4_FEATURE_INCOMPAT_MMP| \ > EXT4_FEATURE_INCOMPAT_LARGEDIR| \ > - 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| \ > @@ -1011,6 +1012,7 @@ struct ext2_dir_entry_tail { > #define EXT2_FT_SYMLINK 7 > > #define EXT2_FT_MAX 8 > +#define EXT2_FT_MASK 0x0f > > /* > * Annoyingly, e2fsprogs always swab16s ext2_dir_entry.name_len, so we > @@ -1028,11 +1030,18 @@ struct ext2_dir_entry_tail { > #define EXT2_DIR_ENTRY_HEADER_LEN 8 > #define EXT2_DIR_PAD 4 > #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) > -#define EXT2_DIR_REC_LEN(name_len) (((name_len) + \ > +#define EXT2_DIR_NAME_LEN(name_len) (((name_len) + \ > EXT2_DIR_ENTRY_HEADER_LEN + \ > EXT2_DIR_ROUND) & \ > ~EXT2_DIR_ROUND) > > +#define EXT2_DIR_REC_LEN(de) (EXT2_DIR_NAME_LEN(((de)->name_len & \ > + EXT2_NAME_LEN) + \ > + ext2_get_dirdata_size(de))) > +/* lu_fid size and NUL char */ > +#define EXT2_DIRENT_LUFID_SIZE 16 > +#define EXT2_DIRENT_LUFID 0x10 > + > /* > * Constants for ext4's extended time encoding > */ > @@ -1091,6 +1100,9 @@ struct mmp_struct { > */ > #define EXT4_MMP_MIN_CHECK_INTERVAL 5 > > +int ext2_get_dirdata_field_size(struct ext2_dir_entry *de, char dirdata_flags); > +int ext2_get_dirent_size(struct ext2_dir_entry *de); > + > /* > * Minimum size of inline data. > */ > @@ -1101,4 +1113,6 @@ struct mmp_struct { > */ > #define EXT4_INLINE_DATA_DOTDOT_SIZE (4) > > +#define EXT2_NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) > + > #endif /* _LINUX_EXT2_FS_H */ > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h > index 6774e32c..fbe4398f 100644 > --- a/lib/ext2fs/ext2fs.h > +++ b/lib/ext2fs/ext2fs.h > @@ -600,6 +600,7 @@ typedef struct ext2_icount *ext2_icount_t; > EXT3_FEATURE_INCOMPAT_EXTENTS|\ > EXT4_FEATURE_INCOMPAT_FLEX_BG|\ > EXT4_FEATURE_INCOMPAT_EA_INODE|\ > + EXT4_FEATURE_INCOMPAT_DIRDATA|\ > EXT4_LIB_INCOMPAT_MMP|\ > EXT4_FEATURE_INCOMPAT_64BIT|\ > EXT4_FEATURE_INCOMPAT_INLINE_DATA|\ > @@ -1978,6 +1979,25 @@ _INLINE_ int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks) > return blocks * ((fs->blocksize - 8) / sizeof(struct ext2_dx_entry)); > } > > +_INLINE_ struct ext2_dx_root_info *get_ext2_dx_root_info(ext2_filsys fs, > + char *buf) > +{ > + struct ext2_dir_entry *de = (struct ext2_dir_entry *)buf; > + > + if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA)) > + return (struct ext2_dx_root_info *)(buf + > + EXT2_DIR_NAME_LEN(1) + > + EXT2_DIR_NAME_LEN(2)); > + > + /* get dotdot first */ > + de = (struct ext2_dir_entry *)((char *)de + de->rec_len); > + > + /* dx root info is after dotdot entry */ > + de = (struct ext2_dir_entry *)((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) > */ > @@ -1997,7 +2017,7 @@ _INLINE_ __u64 ext2fs_div64_ceil(__u64 a, __u64 b) > > _INLINE_ int ext2fs_dirent_name_len(const struct ext2_dir_entry *entry) > { > - return entry->name_len & 0xff; > + return entry->name_len & EXT2_NAME_LEN; > } > > _INLINE_ void ext2fs_dirent_set_name_len(struct ext2_dir_entry *entry, int len) > diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c > index 7215c517..684972b3 100644 > --- a/lib/ext2fs/inline_data.c > +++ b/lib/ext2fs/inline_data.c > @@ -149,7 +149,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, > /* we first check '.' and '..' dir */ > dirent.inode = ino; > dirent.name_len = 1; > - ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); > + ext2fs_set_rec_len(fs, EXT2_DIR_NAME_LEN(2), &dirent); > dirent.name[0] = '.'; > dirent.name[1] = '\0'; > ctx->buf = (char *)&dirent; > @@ -160,7 +160,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, > > dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); > dirent.name_len = 2; > - ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); > + ext2fs_set_rec_len(fs, EXT2_DIR_NAME_LEN(3), &dirent); > dirent.name[0] = '.'; > dirent.name[1] = '.'; > dirent.name[2] = '\0'; > @@ -296,14 +296,14 @@ static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, > ext2fs_dirent_set_name_len(dir, 1); > ext2fs_dirent_set_file_type(dir, filetype); > dir->name[0] = '.'; > - rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); > - dir->rec_len = EXT2_DIR_REC_LEN(1); > + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_NAME_LEN(1); > + dir->rec_len = EXT2_DIR_NAME_LEN(1); > > /* > * Set up entry for '..' > */ > dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len); > - dir->rec_len = EXT2_DIR_REC_LEN(2); > + dir->rec_len = EXT2_DIR_NAME_LEN(2); > dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]); > ext2fs_dirent_set_name_len(dir, 2); > ext2fs_dirent_set_file_type(dir, filetype); > @@ -313,11 +313,11 @@ static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, > /* > * Adjust the last rec_len > */ > - offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2); > + offset = EXT2_DIR_NAME_LEN(1) + EXT2_DIR_NAME_LEN(2); > dir = (struct ext2_dir_entry *) (bbuf + offset); > memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE, > size - EXT4_INLINE_DATA_DOTDOT_SIZE); > - size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) - > + size += EXT2_DIR_NAME_LEN(1) + EXT2_DIR_NAME_LEN(2) - > EXT4_INLINE_DATA_DOTDOT_SIZE; > > do { > diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c > index 65dc8877..a038a104 100644 > --- a/lib/ext2fs/link.c > +++ b/lib/ext2fs/link.c > @@ -47,7 +47,7 @@ static int link_proc(struct ext2_dir_entry *dirent, > if (ls->done) > return DIRENT_ABORT; > > - rec_len = EXT2_DIR_REC_LEN(ls->namelen); > + rec_len = EXT2_DIR_NAME_LEN(ls->namelen); > > ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); > if (ls->err) > @@ -92,8 +92,8 @@ static int link_proc(struct ext2_dir_entry *dirent, > > /* De-convert a dx_root block */ > if (csum_size && > - curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) && > - offset == EXT2_DIR_REC_LEN(1) && > + curr_rec_len == ls->fs->blocksize - EXT2_DIR_NAME_LEN(1) && > + offset == EXT2_DIR_NAME_LEN(1) && > dirent->name[0] == '.' && dirent->name[1] == '.') { > curr_rec_len -= csum_size; > ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); > @@ -110,7 +110,7 @@ static int link_proc(struct ext2_dir_entry *dirent, > * truncate it and return. > */ > if (dirent->inode) { > - min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent)); > + min_rec_len = EXT2_DIR_REC_LEN(dirent); > if (curr_rec_len < (min_rec_len + rec_len)) > return ret; > rec_len = curr_rec_len - min_rec_len; > @@ -138,7 +138,7 @@ static int link_proc(struct ext2_dir_entry *dirent, > ext2fs_dirent_set_name_len(dirent, ls->namelen); > strncpy(dirent->name, ls->name, ls->namelen); > if (ext2fs_has_feature_filetype(ls->sb)) > - ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7); > + ext2fs_dirent_set_file_type(dirent, ls->flags & EXT2_FT_MASK); > > ls->done++; > return DIRENT_ABORT|DIRENT_CHANGED; > diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c > index 7f472850..f168d668 100644 > --- a/lib/ext2fs/newdir.c > +++ b/lib/ext2fs/newdir.c > @@ -64,8 +64,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, > ext2fs_dirent_set_name_len(dir, 1); > ext2fs_dirent_set_file_type(dir, filetype); > dir->name[0] = '.'; > - rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); > - dir->rec_len = EXT2_DIR_REC_LEN(1); > + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_NAME_LEN(1); > + dir->rec_len = EXT2_DIR_NAME_LEN(1); > > /* > * Set up entry for '..' > diff --git a/misc/mke2fs.c b/misc/mke2fs.c > index cfb10bc4..4d8593f7 100644 > --- a/misc/mke2fs.c > +++ b/misc/mke2fs.c > @@ -1084,6 +1084,7 @@ static __u32 ok_features[3] = { > EXT4_FEATURE_INCOMPAT_FLEX_BG| > EXT4_FEATURE_INCOMPAT_EA_INODE| > EXT4_FEATURE_INCOMPAT_MMP | > + EXT4_FEATURE_INCOMPAT_DIRDATA| > EXT4_FEATURE_INCOMPAT_64BIT| > EXT4_FEATURE_INCOMPAT_INLINE_DATA| > EXT4_FEATURE_INCOMPAT_ENCRYPT | > @@ -2900,6 +2901,13 @@ int main (int argc, char *argv[]) > exit(1); > } > > + if (ext2fs_has_feature_inline_data(fs->super) && > + ext2fs_has_feature_dirdata(fs->super)) { > + printf("%s", _("The dirdata feature can not enabled " > + "with inline data feature.\n")); > + exit(1); > + } > + > /* Calculate journal blocks */ > if (!journal_device && ((journal_size) || > ext2fs_has_feature_journal(&fs_param))) > diff --git a/misc/tune2fs.c b/misc/tune2fs.c > index d0a18a18..44dd41a5 100644 > --- a/misc/tune2fs.c > +++ b/misc/tune2fs.c > @@ -157,6 +157,7 @@ static __u32 ok_features[3] = { > EXT4_FEATURE_INCOMPAT_FLEX_BG | > EXT4_FEATURE_INCOMPAT_EA_INODE| > EXT4_FEATURE_INCOMPAT_MMP | > + EXT4_FEATURE_INCOMPAT_DIRDATA | > EXT4_FEATURE_INCOMPAT_64BIT | > EXT4_FEATURE_INCOMPAT_ENCRYPT | > EXT4_FEATURE_INCOMPAT_CSUM_SEED | > @@ -183,6 +184,7 @@ static __u32 clear_ok_features[3] = { > EXT2_FEATURE_INCOMPAT_FILETYPE | > EXT4_FEATURE_INCOMPAT_FLEX_BG | > EXT4_FEATURE_INCOMPAT_MMP | > + EXT4_FEATURE_INCOMPAT_DIRDATA | > EXT4_FEATURE_INCOMPAT_64BIT | > EXT4_FEATURE_INCOMPAT_CSUM_SEED, > /* R/O compat */ > -- > 2.14.3 (Apple Git-98) >
On May 4, 2018, at 1:09 AM, c17828 <artem.blagodarenko@gmail.com> wrote: > > From: Andreas Dilger <andreas.dilger@intel.com> > > Add support for the INCOMPAT_DIRDATA feature, which allows > storing extra data in the directory entry beyond the name. > This allows the Lustre File IDentifier to be accessed in > an efficient manner, and would be useful for expanding a > filesystem to allow more than 2^32 inodes in the future. > > Lustre-bug: https://jira.hpdd.intel.com/browse/LU-4677 > Signed-off-by: Pravin Shelar <pravin@clusterfs.com> > Signed-off-by: Andreas Dilger <andreas.dilger@intel.com> > Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com> > --- > > +/* > + * 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 *de, > + unsigned int offset, struct problem_context *pctx) > +{ > + if (!(ctx->fs->super->s_feature_incompat & > + EXT4_FEATURE_INCOMPAT_DIRDATA)) { This should use: if (ext2fs_has_feature_incompat_dirdata(ctx->fs->super)) { but it wasn't in the original patch since those macros didn't exist back then. However, it looks like the EXT4_FEATURE_INCOMPAT_FUNCS(dirdata, 4, DIRDATA) macro already exists in both e2fsprogs and the kernel, so it doesn't need to be added. > #endif /* _LINUX_EXT2_FS_H */ > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h > index 6774e32c..fbe4398f 100644 > --- a/lib/ext2fs/ext2fs.h > +++ b/lib/ext2fs/ext2fs.h > > +_INLINE_ struct ext2_dx_root_info *get_ext2_dx_root_info(ext2_filsys fs, > + char *buf) > +{ > + struct ext2_dir_entry *de = (struct ext2_dir_entry *)buf; > + > + if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA)) Similarly, this should use the proper macro: if (!ext2fs_has_feature_incompat_dirdata(fs->super)) Cheers, Andreas
diff --git a/debugfs/htree.c b/debugfs/htree.c index cf7d78aa..b7f1add0 100644 --- a/debugfs/htree.c +++ b/debugfs/htree.c @@ -278,7 +278,7 @@ void do_htree_dump(int argc, char *argv[]) goto errout; } - rootnode = (struct ext2_dx_root_info *) (buf + 24); + rootnode = get_ext2_dx_root_info(current_fs, buf); fprintf(pager, "Root node dump:\n"); fprintf(pager, "\t Reserved zero: %u\n", rootnode->reserved_zero); diff --git a/debugfs/ls.c b/debugfs/ls.c index 61b63196..082c14b9 100644 --- a/debugfs/ls.c +++ b/debugfs/ls.c @@ -25,6 +25,30 @@ extern char *optarg; #include "debugfs.h" +#ifndef DFID +#define DFID "[%#llx:0x%x:0x%x]" +#define PFID(fid) ((unsigned long long)fid_seq(fid),\ + fid_oid(fid), fid_ver(fid)) +struct lu_fid { + __u64 f_seq; + __u32 f_oid; + __u32 f_ver; +}; +#endif /* !DFID */ + +#ifndef HAVE_LUSTRE_LUSTRE_IDL_H +#define fid_seq(fid) ((fid)->f_seq) +#define fid_oid(fid) ((fid)->f_oid) +#define fid_ver(fid) ((fid)->f_ver) + +static inline void fid_be_to_cpu(struct lu_fid *dst, struct lu_fid *src) +{ + dst->f_seq = ext2fs_be64_to_cpu(src->f_seq); + dst->f_oid = ext2fs_be32_to_cpu(src->f_oid); + dst->f_ver = ext2fs_be32_to_cpu(src->f_ver); +} +#endif + /* * list directory */ @@ -32,6 +56,7 @@ extern char *optarg; #define LONG_OPT 0x0001 #define PARSE_OPT 0x0002 #define RAW_OPT 0x0004 +#define DIRDATA_OPT 0x0008 #define ENCRYPT_OPT 0x8000 struct list_dir_struct { @@ -44,6 +69,41 @@ struct list_dir_struct { static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static void list_dirdata(struct list_dir_struct *ls, + struct ext2_dir_entry *dirent) +{ + unsigned char *data; + int dlen; + __u8 dirdata_mask; + __u8 file_type = dirent->name_len >> 8; + + data = (unsigned char *)dirent->name + + (dirent->name_len & EXT2_NAME_LEN) + 1; + + for (dirdata_mask = EXT2_FT_MASK + 1; + dirdata_mask != 0; dirdata_mask <<= 1) { + if ((dirdata_mask & file_type) == 0) + continue; + + dlen = data[0]; + + if (dirdata_mask == EXT2_DIRENT_LUFID) { + struct lu_fid *fid = (struct lu_fid *)(data + 1); + + fid_be_to_cpu(fid, fid); + fprintf(ls->f, "fid:"DFID, PFID(fid)); + } else { + int i; + + for (i = 1; i < dlen; i++) + fprintf(ls->f, "%02x", data[i]); + } + + fprintf(ls->f, " "); + data += dlen; + } +} + static int print_filename(FILE *f, struct ext2_dir_entry *dirent, int options) { unsigned char ch; @@ -157,6 +217,8 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), else fprintf(ls->f, "%5llu", EXT2_I_SIZE(&inode)); fprintf(ls->f, " %s ", datestr); + if ((ls->options & DIRDATA_OPT) != 0) + list_dirdata(ls, dirent); print_filename(ls->f, dirent, options); fputc('\n', ls->f); } else { @@ -204,7 +266,7 @@ void do_list_dir(int argc, char *argv[]) return; reset_getopt(); - while ((c = getopt (argc, argv, "cdlpr")) != EOF) { + while ((c = getopt(argc, argv, "cdDlpr")) != EOF) { switch (c) { case 'c': flags |= DIRENT_FLAG_INCLUDE_CSUM; @@ -212,6 +274,9 @@ void do_list_dir(int argc, char *argv[]) case 'l': ls.options |= LONG_OPT; break; + case 'D': + ls.options |= DIRDATA_OPT; + break; case 'd': flags |= DIRENT_FLAG_INCLUDE_REMOVED; break; diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 5015d938..d39086d2 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -719,7 +719,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx, */ memcpy(&dotdot, inode->i_block, sizeof(dotdot)); memcpy(&de, ((char *)inode->i_block) + EXT4_INLINE_DATA_DOTDOT_SIZE, - EXT2_DIR_REC_LEN(0)); + EXT2_DIR_NAME_LEN(0)); dotdot = ext2fs_le32_to_cpu(dotdot); de.inode = ext2fs_le32_to_cpu(de.inode); de.rec_len = ext2fs_le16_to_cpu(de.rec_len); @@ -2646,7 +2646,7 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx, return 1; /* 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)) diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 1b0504c8..c419a016 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -366,13 +366,88 @@ static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) return (int) (db_a->blockcnt - db_b->blockcnt); } +void ext2_fix_dirent_dirdata(struct ext2_dir_entry *de) +{ + __u16 file_type = de->name_len & (EXT2_FT_MASK << 8); + __u8 de_flags = (de->name_len >> 8) & ~EXT2_FT_MASK; + __u8 name_len = de->name_len & EXT2_NAME_LEN; + __u8 new_flag = 0; + int i; + + for (i = 0; i < 4; i++) { + __u8 flags = new_flag | (1 << i) << 4; + + /* new_flag is accumulating flags that are set in de_flags + * and still fit inside rec_len. ext2_get_dirent_dirdata_size() + * returns the size of all the dirdata entries in flags, and + * chops off any that are beyond rec_len. + */ + if ((de_flags & flags) == flags) { + int dirdatalen = ext2_get_dirdata_field_size(de, + flags); + int rlen = EXT2_DIR_NAME_LEN(name_len + dirdatalen); + + if (rlen > de->rec_len) + break; + + new_flag |= flags; + } + } + + de->name_len = name_len | file_type | (new_flag << 8); +} + +/* + * 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 *de, + unsigned int offset, struct problem_context *pctx) +{ + if (!(ctx->fs->super->s_feature_incompat & + EXT4_FEATURE_INCOMPAT_DIRDATA)) { + if ((de->name_len >> 8) & ~EXT2_FT_MASK) { + /* clear dirent extra data flags. */ + if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) { + de->name_len &= (EXT2_FT_MASK << 8) | + EXT2_NAME_LEN; + return 2; + } + } + return 1; + } + if ((de->name_len >> 8) & ~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_dirdata_field_size(de, + EXT2_DIRENT_LUFID) % + EXT2_DIRENT_LUFID_SIZE == 1 /*size*/ + 1 /*NULL*/) + 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_dirdata_field_size(de, + EXT2_DIRENT_LUFID) != + EXT2_DIRENT_LUFID_SIZE) + de->name_len &= ~(EXT2_DIRENT_LUFID << 8); + + 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; @@ -380,6 +455,7 @@ static int check_dot(e2fsck_t ctx, int status = 0; int created = 0; problem_t problem = 0; + int dir_data_error; if (!dirent->inode) problem = PR_2_MISSING_DOT; @@ -389,10 +465,12 @@ static int check_dot(e2fsck_t ctx, else if (dirent->name[1] != '\0') problem = PR_2_DOT_NULL_TERM; + dir_data_error = e2fsck_check_dirent_data(ctx, 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; ext2fs_dirent_set_name_len(dirent, 1); @@ -411,7 +489,7 @@ static int check_dot(e2fsck_t ctx, } if (rec_len > 12) { new_len = rec_len - 12; - if (new_len > 12) { + if (new_len > 12 && dir_data_error) { if (created || fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { nextdir = (struct ext2_dir_entry *) @@ -436,11 +514,12 @@ static int check_dot(e2fsck_t ctx, * 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) { problem_t problem = 0; unsigned int rec_len; + int dir_data_error; if (!dirent->inode) problem = PR_2_MISSING_DOT_DOT; @@ -451,10 +530,12 @@ static int check_dotdot(e2fsck_t ctx, else if (dirent->name[2] != '\0') problem = PR_2_DOT_DOT_NULL_TERM; + dir_data_error = e2fsck_check_dirent_data(ctx, 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 @@ -528,6 +609,12 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, int filetype = ext2fs_dirent_file_type(dirent); int should_be = EXT2_FT_UNKNOWN; struct ext2_inode inode; + __u8 dirdata = 0; + + if (ext2fs_has_feature_dirdata(ctx->fs->super)) { + dirdata = filetype & ~EXT2_FT_MASK; + filetype = filetype & EXT2_FT_MASK; + } if (!ext2fs_has_feature_filetype(ctx->fs->super)) { if (filetype == 0 || @@ -558,8 +645,7 @@ static _INLINE_ int check_filetype(e2fsck_t ctx, if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, pctx) == 0) return 0; - - ext2fs_dirent_set_file_type(dirent, should_be); + ext2fs_dirent_set_file_type(dirent, should_be | dirdata); return 1; } @@ -581,7 +667,7 @@ static void parse_int_node(ext2_filsys fs, int csum_size = 0; 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"); @@ -591,8 +677,8 @@ static void parse_int_node(ext2_filsys fs, printf("\t Indirect levels: %d\n", root->indirect_levels); 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); if (failed_csum && (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || @@ -600,7 +686,7 @@ static void parse_int_node(ext2_filsys fs, &cd->pctx))) goto clear_and_exit; } else { - ent = (struct ext2_dx_entry *) (block_buf+8); + ent = (struct ext2_dx_entry *)(block_buf + 8); if (failed_csum && (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || @@ -608,8 +694,7 @@ static void parse_int_node(ext2_filsys fs, &cd->pctx))) goto clear_and_exit; } - - limit = (struct ext2_dx_countlimit *) ent; + limit = (struct ext2_dx_countlimit *)ent; #ifdef DX_DEBUG printf("Number of entries (count): %d\n", @@ -780,7 +865,6 @@ static void salvage_directory(ext2_filsys fs, } } -#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) { struct ext2_dir_entry *d; @@ -790,11 +874,11 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) d = dirbuf; top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize); - while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top) - d = NEXT_DIRENT(d); + while (d->rec_len && !(d->rec_len & 0x3) && EXT2_NEXT_DIRENT(d) <= top) + d = EXT2_NEXT_DIRENT(d); if (d != top) { - unsigned int min_size = EXT2_DIR_REC_LEN( + unsigned int min_size = EXT2_DIR_NAME_LEN( ext2fs_dirent_name_len(dirbuf)); if (min_size > (char *)top - (char *)d) return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; @@ -809,7 +893,6 @@ static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) return 0; } -#undef NEXT_DIRENT static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, size_t *inline_data_size, @@ -828,7 +911,7 @@ static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, */ if (old_size > EXT4_MIN_INLINE_DATA_SIZE && old_size < EXT4_MIN_INLINE_DATA_SIZE + - EXT2_DIR_REC_LEN(1)) { + EXT2_DIR_NAME_LEN(1)) { old_size = EXT4_MIN_INLINE_DATA_SIZE; new_size = old_size; } else @@ -1035,7 +1118,7 @@ inline_read_fail: if (((inline_data_size & 3) || (inline_data_size > EXT4_MIN_INLINE_DATA_SIZE && inline_data_size < EXT4_MIN_INLINE_DATA_SIZE + - EXT2_DIR_REC_LEN(1))) && + EXT2_DIR_NAME_LEN(1))) && fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) { errcode_t err = fix_inline_dir_size(ctx, ino, &inline_data_size, &pctx, @@ -1085,7 +1168,7 @@ inline_read_fail: (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 || @@ -1165,7 +1248,7 @@ skip_checksum: * force salvaging this dir. */ if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN) - rec_len = EXT2_DIR_REC_LEN(1); + rec_len = EXT2_DIR_NAME_LEN(1); else (void) ext2fs_get_rec_len(fs, dirent, &rec_len); cd->pctx.dirent = dirent; @@ -1227,7 +1310,7 @@ skip_checksum: memset(&dot, 0, sizeof(dot)); dirent = ˙ dirent->inode = ino; - dirent->rec_len = EXT2_DIR_REC_LEN(1); + dirent->rec_len = EXT2_DIR_NAME_LEN(1); dirent->name_len = 1 | filetype; dirent->name[0] = '.'; } else if (dot_state == 1) { @@ -1235,7 +1318,7 @@ skip_checksum: dirent = &dotdot; dirent->inode = ((struct ext2_dir_entry *)buf)->inode; - dirent->rec_len = EXT2_DIR_REC_LEN(2); + dirent->rec_len = EXT2_DIR_NAME_LEN(2); dirent->name_len = 2 | filetype; dirent->name[0] = '.'; dirent->name[1] = '.'; @@ -1247,10 +1330,10 @@ skip_checksum: } 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) @@ -1266,6 +1349,10 @@ skip_checksum: if (!dirent->inode) goto next; + ret = e2fsck_check_dirent_data(ctx, dirent, offset, &cd->pctx); + if (ret == 2) + dir_modified++; + /* * Make sure the inode listed is a legal one. */ diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c index 6a975b36..4a777213 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -698,6 +698,7 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent, struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data; errcode_t retval; struct problem_context pctx; + __u16 dirdata = 0; if (ext2fs_dirent_name_len(dirent) != 2) return 0; @@ -717,11 +718,17 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent, fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); } dirent->inode = fp->parent; + + dirdata = dirent->name_len & (~EXT2_FT_MASK << 8); + if (ext2fs_has_feature_filetype(fp->ctx->fs->super)) ext2fs_dirent_set_file_type(dirent, EXT2_FT_DIR); else ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); + if (ext2fs_has_feature_dirdata(fp->ctx->fs->super)) + dirent->name_len |= dirdata; + fp->done++; return DIRENT_ABORT | DIRENT_CHANGED; } diff --git a/e2fsck/problem.c b/e2fsck/problem.c index edc9d51f..2a86d528 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -1671,6 +1671,11 @@ static struct e2fsck_problem problem_table[] = { N_("Encrypted @E is too short.\n"), PROMPT_CLEAR, 0 }, + /* 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 */ diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 482d111a..05214840 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -1004,6 +1004,9 @@ struct problem_context { /* Encrypted directory entry is too short */ #define PR_2_BAD_ENCRYPTED_NAME 0x020050 +/* Entry dirdata length set incorrectly */ +#define PR_2_CLEAR_DIRDATA 0x020051 + /* * Pass 3 errors */ diff --git a/e2fsck/rehash.c b/e2fsck/rehash.c index 486e1f21..546b073c 100644 --- a/e2fsck/rehash.c +++ b/e2fsck/rehash.c @@ -85,6 +85,8 @@ struct fill_dir_struct { int compress; ino_t parent; ext2_ino_t dir; + struct ext2_dir_entry *dot_de; + struct ext2_dir_entry *dotdot_de; }; struct hash_entry { @@ -160,11 +162,14 @@ static int fill_dir_block(ext2_filsys fs, if (dirent->inode == 0) continue; if (!fd->compress && (name_len == 1) && - (dirent->name[0] == '.')) + (dirent->name[0] == '.')) { + fd->dot_de = dirent; continue; + } if (!fd->compress && (name_len == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { fd->parent = dirent->inode; + fd->dotdot_de = dirent; continue; } if (fd->num_array >= fd->max_array) { @@ -179,7 +184,7 @@ static int fill_dir_block(ext2_filsys fs, } ent = fd->harray + fd->num_array++; ent->dir = dirent; - fd->dir_size += EXT2_DIR_REC_LEN(name_len); + fd->dir_size += EXT2_DIR_REC_LEN(dirent); ent->ino = dirent->inode; if (fd->compress) ent->hash = ent->minor_hash = 0; @@ -475,7 +480,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, ent = fd->harray + i; if (ent->dir->inode == 0) continue; - rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir)); + rec_len = EXT2_DIR_REC_LEN(ent->dir); if (rec_len > left) { if (left) { left += prev_rec_len; @@ -510,8 +515,7 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, if (retval) return retval; prev_rec_len = rec_len; - memcpy(dirent->name, ent->dir->name, - ext2fs_dirent_name_len(dirent)); + memcpy(dirent->name, ent->dir->name, rec_len); offset += rec_len; left -= rec_len; if (left < slack) { @@ -536,44 +540,49 @@ static errcode_t copy_dir_entries(e2fsck_t ctx, static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf, - ext2_ino_t ino, ext2_ino_t parent) + ext2_ino_t ino, ext2_ino_t parent, + struct ext2_dir_entry *dot_de, + struct ext2_dir_entry *dotdot_de) { - struct ext2_dir_entry *dir; - struct ext2_dx_root_info *root; + struct ext2_dir_entry *dirent; + struct ext2_dx_root_info *root; struct ext2_dx_countlimit *limits; - int filetype = 0; int csum_size = 0; - - if (ext2fs_has_feature_filetype(fs->super)) - filetype = EXT2_FT_DIR; + int offset; + int rec_len; memset(buf, 0, fs->blocksize); - dir = (struct ext2_dir_entry *) buf; - dir->inode = ino; - dir->name[0] = '.'; - ext2fs_dirent_set_name_len(dir, 1); - ext2fs_dirent_set_file_type(dir, filetype); - dir->rec_len = 12; - dir = (struct ext2_dir_entry *) (buf + 12); - dir->inode = parent; - dir->name[0] = '.'; - dir->name[1] = '.'; - ext2fs_dirent_set_name_len(dir, 2); - ext2fs_dirent_set_file_type(dir, filetype); - dir->rec_len = fs->blocksize - 12; - - root = (struct ext2_dx_root_info *) (buf+24); + dirent = (struct ext2_dir_entry *) buf; + dirent->inode = ino; + + dirent->name_len = dot_de->name_len; + offset = rec_len = dirent->rec_len = dot_de->rec_len; + memcpy(dirent->name, dot_de->name, rec_len); + + dirent = EXT2_NEXT_DIRENT(dirent); + /* set to jump over the index block */ + + dirent->inode = parent; + + dirent->name_len = dotdot_de->name_len; + dirent->rec_len = fs->blocksize - rec_len; + rec_len = EXT2_DIR_REC_LEN(dotdot_de); + memcpy(dirent->name, dotdot_de->name, rec_len); + offset += rec_len; + + root = (struct ext2_dx_root_info *)(buf + offset); root->reserved_zero = 0; root->hash_version = fs->super->s_def_hash_version; - root->info_length = 8; + root->info_length = sizeof(*root); root->indirect_levels = 0; root->unused_flags = 0; + offset += root->info_length; if (ext2fs_has_feature_metadata_csum(fs->super)) csum_size = sizeof(struct ext2_dx_tail); - limits = (struct ext2_dx_countlimit *) (buf+32); - limits->limit = (fs->blocksize - (32 + csum_size)) / + limits = (struct ext2_dx_countlimit *) (buf + offset); + limits->limit = (fs->blocksize - (offset + csum_size)) / sizeof(struct ext2_dx_entry); limits->count = 0; @@ -647,7 +656,9 @@ static int alloc_blocks(ext2_filsys fs, static errcode_t calculate_tree(ext2_filsys fs, struct out_dir *outdir, ext2_ino_t ino, - ext2_ino_t parent) + ext2_ino_t parent, + struct ext2_dir_entry *dot_de, + struct ext2_dir_entry *dotdot_de) { struct ext2_dx_root_info *root_info; struct ext2_dx_entry *root, *int_ent, *dx_ent = 0; @@ -657,7 +668,9 @@ static errcode_t calculate_tree(ext2_filsys fs, int i, c1, c2, c3, nblks; int limit_offset, int_offset, root_offset; - root_info = set_root_node(fs, outdir->buf, ino, parent); + root_info = set_root_node(fs, outdir->buf, ino, parent, dot_de, + dotdot_de); + root_offset = limit_offset = ((char *) root_info - outdir->buf) + root_info->info_length; root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset); @@ -944,11 +957,10 @@ resort: if (retval) goto errout; - free(dir_buf); dir_buf = 0; - if (!fd.compress) { /* Calculate the interior nodes */ - retval = calculate_tree(fs, &outdir, ino, fd.parent); + retval = calculate_tree(fs, &outdir, ino, fd.parent, + fd.dot_de, fd.dotdot_de); if (retval) goto errout; } diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c index 54b27772..3563138d 100644 --- a/lib/ext2fs/dirblock.c +++ b/lib/ext2fs/dirblock.c @@ -50,6 +50,39 @@ errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block, return ext2fs_read_dir_block4(fs, block, buf, flags, 0); } +/* + * Compute the dirdata length. This includes only 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_dirdata_field_size(struct ext2_dir_entry *de, + char dirdata_flags) +{ + char *lenp = de->name + (de->name_len & EXT2_NAME_LEN) + 1 /* NUL */; + __u8 extra_data_flags = (de->name_len & ~(EXT2_FT_MASK << 8)) >> 12; + int dlen = 0; + + dirdata_flags >>= 4; + while ((extra_data_flags & dirdata_flags) != 0) { + if (extra_data_flags & 1) { + if (dirdata_flags & 1) + dlen += *lenp; + + lenp += *lenp; + } + extra_data_flags >>= 1; + dirdata_flags >>= 1; + } + + /* add NUL terminator byte to dirdata length */ + return dlen + (dlen != 0); +} + +int ext2_get_dirdata_size(struct ext2_dir_entry *de) +{ + return ext2_get_dirdata_field_size(de, ~EXT2_FT_MASK); +} + errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags EXT2FS_ATTR((unused))) { diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 2496d16d..4919f946 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -923,7 +923,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, 4, ENCRYPT) #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT4_FEATURE_INCOMPAT_MMP| \ EXT4_FEATURE_INCOMPAT_LARGEDIR| \ - 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| \ @@ -1011,6 +1012,7 @@ struct ext2_dir_entry_tail { #define EXT2_FT_SYMLINK 7 #define EXT2_FT_MAX 8 +#define EXT2_FT_MASK 0x0f /* * Annoyingly, e2fsprogs always swab16s ext2_dir_entry.name_len, so we @@ -1028,11 +1030,18 @@ struct ext2_dir_entry_tail { #define EXT2_DIR_ENTRY_HEADER_LEN 8 #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) -#define EXT2_DIR_REC_LEN(name_len) (((name_len) + \ +#define EXT2_DIR_NAME_LEN(name_len) (((name_len) + \ EXT2_DIR_ENTRY_HEADER_LEN + \ EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) +#define EXT2_DIR_REC_LEN(de) (EXT2_DIR_NAME_LEN(((de)->name_len & \ + EXT2_NAME_LEN) + \ + ext2_get_dirdata_size(de))) +/* lu_fid size and NUL char */ +#define EXT2_DIRENT_LUFID_SIZE 16 +#define EXT2_DIRENT_LUFID 0x10 + /* * Constants for ext4's extended time encoding */ @@ -1091,6 +1100,9 @@ struct mmp_struct { */ #define EXT4_MMP_MIN_CHECK_INTERVAL 5 +int ext2_get_dirdata_field_size(struct ext2_dir_entry *de, char dirdata_flags); +int ext2_get_dirent_size(struct ext2_dir_entry *de); + /* * Minimum size of inline data. */ @@ -1101,4 +1113,6 @@ struct mmp_struct { */ #define EXT4_INLINE_DATA_DOTDOT_SIZE (4) +#define EXT2_NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) + #endif /* _LINUX_EXT2_FS_H */ diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 6774e32c..fbe4398f 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -600,6 +600,7 @@ typedef struct ext2_icount *ext2_icount_t; EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG|\ EXT4_FEATURE_INCOMPAT_EA_INODE|\ + EXT4_FEATURE_INCOMPAT_DIRDATA|\ EXT4_LIB_INCOMPAT_MMP|\ EXT4_FEATURE_INCOMPAT_64BIT|\ EXT4_FEATURE_INCOMPAT_INLINE_DATA|\ @@ -1978,6 +1979,25 @@ _INLINE_ int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks) return blocks * ((fs->blocksize - 8) / sizeof(struct ext2_dx_entry)); } +_INLINE_ struct ext2_dx_root_info *get_ext2_dx_root_info(ext2_filsys fs, + char *buf) +{ + struct ext2_dir_entry *de = (struct ext2_dir_entry *)buf; + + if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA)) + return (struct ext2_dx_root_info *)(buf + + EXT2_DIR_NAME_LEN(1) + + EXT2_DIR_NAME_LEN(2)); + + /* get dotdot first */ + de = (struct ext2_dir_entry *)((char *)de + de->rec_len); + + /* dx root info is after dotdot entry */ + de = (struct ext2_dir_entry *)((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) */ @@ -1997,7 +2017,7 @@ _INLINE_ __u64 ext2fs_div64_ceil(__u64 a, __u64 b) _INLINE_ int ext2fs_dirent_name_len(const struct ext2_dir_entry *entry) { - return entry->name_len & 0xff; + return entry->name_len & EXT2_NAME_LEN; } _INLINE_ void ext2fs_dirent_set_name_len(struct ext2_dir_entry *entry, int len) diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c index 7215c517..684972b3 100644 --- a/lib/ext2fs/inline_data.c +++ b/lib/ext2fs/inline_data.c @@ -149,7 +149,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, /* we first check '.' and '..' dir */ dirent.inode = ino; dirent.name_len = 1; - ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); + ext2fs_set_rec_len(fs, EXT2_DIR_NAME_LEN(2), &dirent); dirent.name[0] = '.'; dirent.name[1] = '\0'; ctx->buf = (char *)&dirent; @@ -160,7 +160,7 @@ int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); dirent.name_len = 2; - ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); + ext2fs_set_rec_len(fs, EXT2_DIR_NAME_LEN(3), &dirent); dirent.name[0] = '.'; dirent.name[1] = '.'; dirent.name[2] = '\0'; @@ -296,14 +296,14 @@ static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, ext2fs_dirent_set_name_len(dir, 1); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; - rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); - dir->rec_len = EXT2_DIR_REC_LEN(1); + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_NAME_LEN(1); + dir->rec_len = EXT2_DIR_NAME_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len); - dir->rec_len = EXT2_DIR_REC_LEN(2); + dir->rec_len = EXT2_DIR_NAME_LEN(2); dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]); ext2fs_dirent_set_name_len(dir, 2); ext2fs_dirent_set_file_type(dir, filetype); @@ -313,11 +313,11 @@ static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, /* * Adjust the last rec_len */ - offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2); + offset = EXT2_DIR_NAME_LEN(1) + EXT2_DIR_NAME_LEN(2); dir = (struct ext2_dir_entry *) (bbuf + offset); memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE, size - EXT4_INLINE_DATA_DOTDOT_SIZE); - size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) - + size += EXT2_DIR_NAME_LEN(1) + EXT2_DIR_NAME_LEN(2) - EXT4_INLINE_DATA_DOTDOT_SIZE; do { diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c index 65dc8877..a038a104 100644 --- a/lib/ext2fs/link.c +++ b/lib/ext2fs/link.c @@ -47,7 +47,7 @@ static int link_proc(struct ext2_dir_entry *dirent, if (ls->done) return DIRENT_ABORT; - rec_len = EXT2_DIR_REC_LEN(ls->namelen); + rec_len = EXT2_DIR_NAME_LEN(ls->namelen); ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); if (ls->err) @@ -92,8 +92,8 @@ static int link_proc(struct ext2_dir_entry *dirent, /* De-convert a dx_root block */ if (csum_size && - curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) && - offset == EXT2_DIR_REC_LEN(1) && + curr_rec_len == ls->fs->blocksize - EXT2_DIR_NAME_LEN(1) && + offset == EXT2_DIR_NAME_LEN(1) && dirent->name[0] == '.' && dirent->name[1] == '.') { curr_rec_len -= csum_size; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); @@ -110,7 +110,7 @@ static int link_proc(struct ext2_dir_entry *dirent, * truncate it and return. */ if (dirent->inode) { - min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent)); + min_rec_len = EXT2_DIR_REC_LEN(dirent); if (curr_rec_len < (min_rec_len + rec_len)) return ret; rec_len = curr_rec_len - min_rec_len; @@ -138,7 +138,7 @@ static int link_proc(struct ext2_dir_entry *dirent, ext2fs_dirent_set_name_len(dirent, ls->namelen); strncpy(dirent->name, ls->name, ls->namelen); if (ext2fs_has_feature_filetype(ls->sb)) - ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7); + ext2fs_dirent_set_file_type(dirent, ls->flags & EXT2_FT_MASK); ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c index 7f472850..f168d668 100644 --- a/lib/ext2fs/newdir.c +++ b/lib/ext2fs/newdir.c @@ -64,8 +64,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2fs_dirent_set_name_len(dir, 1); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; - rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); - dir->rec_len = EXT2_DIR_REC_LEN(1); + rec_len = (fs->blocksize - csum_size) - EXT2_DIR_NAME_LEN(1); + dir->rec_len = EXT2_DIR_NAME_LEN(1); /* * Set up entry for '..' diff --git a/misc/mke2fs.c b/misc/mke2fs.c index cfb10bc4..4d8593f7 100644 --- a/misc/mke2fs.c +++ b/misc/mke2fs.c @@ -1084,6 +1084,7 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG| EXT4_FEATURE_INCOMPAT_EA_INODE| EXT4_FEATURE_INCOMPAT_MMP | + EXT4_FEATURE_INCOMPAT_DIRDATA| EXT4_FEATURE_INCOMPAT_64BIT| EXT4_FEATURE_INCOMPAT_INLINE_DATA| EXT4_FEATURE_INCOMPAT_ENCRYPT | @@ -2900,6 +2901,13 @@ int main (int argc, char *argv[]) exit(1); } + if (ext2fs_has_feature_inline_data(fs->super) && + ext2fs_has_feature_dirdata(fs->super)) { + printf("%s", _("The dirdata feature can not enabled " + "with inline data feature.\n")); + exit(1); + } + /* Calculate journal blocks */ if (!journal_device && ((journal_size) || ext2fs_has_feature_journal(&fs_param))) diff --git a/misc/tune2fs.c b/misc/tune2fs.c index d0a18a18..44dd41a5 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -157,6 +157,7 @@ static __u32 ok_features[3] = { EXT4_FEATURE_INCOMPAT_FLEX_BG | EXT4_FEATURE_INCOMPAT_EA_INODE| EXT4_FEATURE_INCOMPAT_MMP | + EXT4_FEATURE_INCOMPAT_DIRDATA | EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_ENCRYPT | EXT4_FEATURE_INCOMPAT_CSUM_SEED | @@ -183,6 +184,7 @@ static __u32 clear_ok_features[3] = { EXT2_FEATURE_INCOMPAT_FILETYPE | EXT4_FEATURE_INCOMPAT_FLEX_BG | EXT4_FEATURE_INCOMPAT_MMP | + EXT4_FEATURE_INCOMPAT_DIRDATA | EXT4_FEATURE_INCOMPAT_64BIT | EXT4_FEATURE_INCOMPAT_CSUM_SEED, /* R/O compat */