Whamcloud - gitweb
e2fsck: add support for dirdata feature
authorAndreas Dilger <andreas.dilger@intel.com>
Fri, 13 Apr 2012 19:39:26 +0000 (13:39 -0600)
committerAndreas Dilger <andreas.dilger@intel.com>
Tue, 10 May 2016 05:12:32 +0000 (23:12 -0600)
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.

  LU-1774 e2fsck: e2fsck -D does not change dirdata content

  Fix dir optimization to preserver dirdata content for dot
  and dotdot entries.

Signed-off-by: Bobi Jam <bobijam.xu@intel.com>
  Change-Id: Iae190794da75a2080a8e5cc5b95a49e0c894f72f

  LU-2462 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.

Signed-off-by: Manisha Salve <msalve@ddn.com>
  Change-Id: Ic593c558c47a78183143ec8e99d8385ac94d06f7

  LU-4677 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.

Signed-off-by: Pravin Shelar <pravin@clusterfs.com>
Signed-off-by: Andreas Dilger <andreas.dilger@intel.com>
15 files changed:
debugfs/ls.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/pass3.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/rehash.c
lib/ext2fs/dirblock.c
lib/ext2fs/ext2_fs.h
lib/ext2fs/ext2fs.h
lib/ext2fs/lfsck.h [new file with mode: 0644]
lib/ext2fs/link.c
lib/ext2fs/newdir.c
misc/mke2fs.c
misc/tune2fs.c

index b4036de..0996937 100644 (file)
@@ -24,6 +24,7 @@ extern char *optarg;
 #endif
 
 #include "debugfs.h"
+#include "ext2fs/lfsck.h"
 
 /*
  * list directory
@@ -32,6 +33,7 @@ extern char *optarg;
 #define LONG_OPT       0x0001
 #define DELETED_OPT    0x0002
 #define PARSE_OPT      0x0004
+#define DIRDATA_OPT    0x0008
 
 struct list_dir_struct {
        FILE    *f;
@@ -42,6 +44,40 @@ 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];
+               fprintf(ls->f, " ");
+
+               if (dirdata_mask == EXT2_DIRENT_LUFID) {
+                       struct lu_fid *fid = (struct lu_fid *)(data + 1);
+
+                       fid_be_to_cpu(fid, fid);
+                       fprintf(ls->f, DFID, PFID(fid));
+               } else {
+                       int i;
+
+                       for (i = 1; i < dlen; i++)
+                               fprintf(ls->f, "%02x", data[i]);
+               }
+               data += dlen;
+       }
+}
+
 static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
                         int    entry,
                         struct ext2_dir_entry *dirent,
@@ -106,7 +142,10 @@ static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
                        fprintf(ls->f, "%5d", inode.i_size);
                else
                        fprintf(ls->f, "%5llu", EXT2_I_SIZE(&inode));
-               fprintf (ls->f, " %s %s\n", datestr, name);
+               fprintf(ls->f, " %s", datestr);
+               if ((ls->options & DIRDATA_OPT) != 0)
+                       list_dirdata(ls, dirent);
+               fprintf(ls->f, " %s\n", name);
        } else {
                sprintf(tmp, "%c%u%c (%d) %s   ", lbr, dirent->inode, rbr,
                        dirent->rec_len, name);
@@ -135,11 +174,14 @@ void do_list_dir(int argc, char *argv[])
                return;
 
        reset_getopt();
-       while ((c = getopt (argc, argv, "dlp")) != EOF) {
+       while ((c = getopt (argc, argv, "dDlp")) != EOF) {
                switch (c) {
                case 'l':
                        ls.options |= LONG_OPT;
                        break;
+               case 'D':
+                       ls.options |= DIRDATA_OPT;
+                       break;
                case 'd':
                        ls.options |= DELETED_OPT;
                        break;
index e1d5c02..9bc35ee 100644 (file)
@@ -2124,7 +2124,7 @@ static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
        }
 
        /* 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))
index ebf35f2..ff02f32 100644 (file)
@@ -335,13 +335,85 @@ 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_dirent_dirdata_size(de,flags);
+                       int rlen = __EXT2_DIR_REC_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_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->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;
@@ -349,6 +421,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;
@@ -358,10 +431,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;
                        dirent->name_len = 1;
@@ -377,7 +452,7 @@ static int check_dot(e2fsck_t ctx,
                        status = 1;
                }
        }
-       if (rec_len > 12) {
+       if (rec_len > 12 && dir_data_error) {
                new_len = rec_len - 12;
                if (new_len > 12) {
                        if (created ||
@@ -402,11 +477,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;
@@ -417,10 +493,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
@@ -482,6 +560,12 @@ static _INLINE_ int check_filetype(e2fsck_t ctx,
        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)) {
@@ -516,7 +600,8 @@ static _INLINE_ int check_filetype(e2fsck_t ctx,
                        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;
 }
 
@@ -538,7 +623,7 @@ static void parse_int_node(ext2_filsys fs,
        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");
@@ -549,11 +634,12 @@ static void parse_int_node(ext2_filsys fs,
                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);
+               ent = (struct ext2_dx_entry *)(block_buf + 8);
        }
-       limit = (struct ext2_dx_countlimit *) ent;
+       limit = (struct ext2_dx_countlimit *)ent;
 
 #ifdef DX_DEBUG
        printf("Number of entries (count): %d\n",
@@ -813,7 +899,7 @@ static int check_dir_block(ext2_filsys fs,
                (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 ||
@@ -864,10 +950,10 @@ out_htree:
                }
 
                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)
@@ -883,6 +969,10 @@ out_htree:
                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.
                 */
index 08e0b8d..0cb51b0 100644 (file)
@@ -634,6 +634,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 ((dirent->name_len & 0xFF) != 2)
                return 0;
@@ -653,6 +654,9 @@ 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 (fp->ctx->fs->super->s_feature_incompat &
            EXT2_FEATURE_INCOMPAT_FILETYPE)
                dirent->name_len = (dirent->name_len & 0xFF) |
@@ -660,6 +664,9 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
        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;
 }
index 812cc18..a56c126 100644 (file)
@@ -1492,6 +1492,11 @@ static struct e2fsck_problem problem_table[] = {
          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 */
index 5d71557..c1afb84 100644 (file)
@@ -909,6 +909,9 @@ struct problem_context {
 /* Inode completely corrupt */
 #define PR_2_INODE_TOOBAD              0x020049
 
+/* Directory dirdata flag set */
+#define PR_2_CLEAR_DIRDATA             0x02f000
+
 /*
  * Pass 3 errors
  */
index 8ff4883..b544370 100644 (file)
@@ -62,6 +62,8 @@ struct fill_dir_struct {
        unsigned int dir_size;
        int compress;
        ino_t parent;
+       struct ext2_dir_entry *dot_de;
+       struct ext2_dir_entry *dotdot_de;
 };
 
 struct hash_entry {
@@ -129,12 +131,15 @@ static int fill_dir_block(ext2_filsys fs,
                dir_offset += rec_len;
                if (dirent->inode == 0)
                        continue;
-               if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
-                   (dirent->name[0] == '.'))
+               if (!fd->compress && ((dirent->name_len & 0xFF) == 1) &&
+                   (dirent->name[0] == '.')) {
+                       fd->dot_de = dirent;
                        continue;
-               if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
+               }
+               if (!fd->compress && ((dirent->name_len & 0xFF) == 2) &&
                    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
                        fd->parent = dirent->inode;
+                       fd->dotdot_de = dirent;
                        continue;
                }
                if (fd->num_array >= fd->max_array) {
@@ -149,7 +154,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(dirent->name_len & 0xFF);
+               fd->dir_size += EXT2_DIR_REC_LEN(dirent);
                ent->ino = dirent->inode;
                if (fd->compress)
                        ent->hash = ent->minor_hash = 0;
@@ -440,7 +445,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(ent->dir->name_len & 0xFF);
+               rec_len = EXT2_DIR_REC_LEN(ent->dir);
                if (rec_len > left) {
                        if (left) {
                                left += prev_rec_len;
@@ -467,7 +472,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, dirent->name_len & 0xFF);
+               memcpy(dirent->name, ent->dir->name, rec_len);
                offset += rec_len;
                left -= rec_len;
                if (left < slack) {
@@ -488,38 +493,44 @@ 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           *dir;
+       struct ext2_dx_root_info        *root;
        struct ext2_dx_countlimit       *limits;
-       int                             filetype = 0;
-
-       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
-               filetype = EXT2_FT_DIR << 8;
+       int                             offset;
+       int                             rec_len;
 
        memset(buf, 0, fs->blocksize);
        dir = (struct ext2_dir_entry *) buf;
        dir->inode = ino;
-       dir->name[0] = '.';
-       dir->name_len = 1 | filetype;
-       dir->rec_len = 12;
-       dir = (struct ext2_dir_entry *) (buf + 12);
+       dir->name_len = dot_de->name_len;
+       dir->rec_len = dot_de->rec_len;
+       rec_len = EXT2_DIR_REC_LEN(dot_de);
+       memcpy(dir->name, dot_de->name, rec_len);
+       offset = rec_len;
+
+       dir = (struct ext2_dir_entry *) (buf + offset);
+       /* set to jump over the index block */
        dir->inode = parent;
-       dir->name[0] = '.';
-       dir->name[1] = '.';
-       dir->name_len = 2 | filetype;
-       dir->rec_len = fs->blocksize - 12;
+       dir->name_len = dotdot_de->name_len;
+       dir->rec_len = fs->blocksize - rec_len;
+       rec_len = EXT2_DIR_REC_LEN(dotdot_de);
+       memcpy(dir->name, dotdot_de->name, rec_len);
+       offset += rec_len;
 
-       root = (struct ext2_dx_root_info *) (buf+24);
+       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(struct ext2_dx_root_info);
        root->indirect_levels = 0;
        root->unused_flags = 0;
+       offset += sizeof(struct ext2_dx_root_info);
 
-       limits = (struct ext2_dx_countlimit *) (buf+32);
-       limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+       limits = (struct ext2_dx_countlimit *) (buf + offset);
+       limits->limit = (fs->blocksize - offset) / sizeof(struct ext2_dx_entry);
        limits->count = 0;
 
        return root;
@@ -550,7 +561,9 @@ static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
 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, *dx_ent = 0;
@@ -560,7 +573,8 @@ static errcode_t calculate_tree(ext2_filsys fs,
        int                             i, c1, c2, nblks;
        int                             limit_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);
@@ -804,11 +818,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;
        }
index cb3a104..c73d6ad 100644 (file)
@@ -28,7 +28,6 @@ errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
        struct ext2_dir_entry *dirent;
        unsigned int    name_len, rec_len;
 
-
        retval = io_channel_read_blk64(fs->io, block, 1, buf);
        if (retval)
                return retval;
@@ -59,6 +58,40 @@ errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
        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 *de,
+                                char dirdata_flags)
+{
+       char *len = 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 += *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 *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)))
 {
index cbc230f..1e5aaa8 100644 (file)
@@ -742,7 +742,8 @@ struct ext2_super_block {
 #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| \
@@ -812,6 +813,7 @@ struct ext2_dir_entry_2 {
 #define EXT2_FT_SYMLINK                7
 
 #define EXT2_FT_MAX            8
+#define EXT2_FT_MASK           0x0f
 
 /*
  * EXT2_DIR_PAD defines the directory entries boundaries
@@ -820,9 +822,16 @@ struct ext2_dir_entry_2 {
  */
 #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_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.
@@ -872,4 +881,7 @@ struct mmp_struct {
  */
 #define EXT4_MMP_MIN_CHECK_INTERVAL     5
 
+int ext2_get_dirent_dirdata_size(struct ext2_dir_entry *de, char dirdata_flags);
+int ext2_get_dirent_size(struct ext2_dir_entry *de);
+
 #endif /* _LINUX_EXT2_FS_H */
index 501b610..e66d2fc 100644 (file)
@@ -578,6 +578,7 @@ typedef struct ext2_icount *ext2_icount_t;
                                         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|\
@@ -588,6 +589,7 @@ typedef struct ext2_icount *ext2_icount_t;
                                         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
@@ -1758,6 +1760,25 @@ _INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
        return (blk_t) 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 *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_REC_LEN(1) +
+                                                   __EXT2_DIR_REC_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)
  */
diff --git a/lib/ext2fs/lfsck.h b/lib/ext2fs/lfsck.h
new file mode 100644 (file)
index 0000000..6a49042
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef LFSCK_H
+#define LFSCK_H
+
+/* This is unfortunately needed for older lustre_user.h to be usable */
+#define LASSERT(cond)          do { } while (0)
+
+#ifdef HAVE_LUSTRE_LUSTREAPI_H
+#include <lustre/lustreapi.h>
+#elif HAVE_LUSTRE_LIBLUSTREAPI_H
+#include <lustre/liblustreapi.h>
+#endif
+
+#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 */
+
+/* Unfortunately, neither the 1.8 or 2.x lustre_idl.h file is suitable
+ * for inclusion by userspace programs because of external dependencies.
+ * Define the minimum set of replacement functions here until that is fixed. */
+#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
+
+#endif /* LFSCK_H */
index bf3c859..867a1f0 100644 (file)
@@ -45,7 +45,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_REC_LEN(ls->namelen);
 
        ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
        if (ls->err)
@@ -72,7 +72,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
         * 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);
                if (curr_rec_len < (min_rec_len + rec_len))
                        return ret;
                rec_len = curr_rec_len - min_rec_len;
@@ -99,7 +99,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
        dirent->name_len = ls->namelen;
        strncpy(dirent->name, ls->name, ls->namelen);
        if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
-               dirent->name_len |= (ls->flags & 0x7) << 8;
+               dirent->name_len |= (ls->flags & EXT2_FT_MASK) << 8;
 
        ls->done++;
        return DIRENT_ABORT|DIRENT_CHANGED;
index 3e2c0db..01d271f 100644 (file)
@@ -59,8 +59,8 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
                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 '..'
index c92ff58..c0bf012 100644 (file)
@@ -1034,6 +1034,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,
        /* R/O compat */
        EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
index ddc7880..e48e641 100644 (file)
@@ -143,7 +143,8 @@ static __u32 ok_features[3] = {
                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|
@@ -165,7 +166,8 @@ static __u32 clear_ok_features[3] = {
        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|