@@ -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);
@@ -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, 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;
@@ -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_REC_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))
@@ -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_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_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_REC_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_REC_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_REC_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_REC_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_REC_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_REC_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.
*/
@@ -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;
}
@@ -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 */
@@ -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
*/
@@ -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;
}
@@ -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)))
{
@@ -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_REC_LEN(name_len) (((name_len) + \
EXT2_DIR_ENTRY_HEADER_LEN + \
EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
+#define EXT2_DIR_REC_LEN(de) (__EXT2_DIR_REC_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 */
@@ -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_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)
*/
@@ -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)
@@ -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_REC_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_REC_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_REC_LEN(1);
+ dir->rec_len = __EXT2_DIR_REC_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_REC_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_REC_LEN(1) + __EXT2_DIR_REC_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_REC_LEN(1) + __EXT2_DIR_REC_LEN(2) -
EXT4_INLINE_DATA_DOTDOT_SIZE;
do {
@@ -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_REC_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_REC_LEN(1) &&
+ offset == __EXT2_DIR_REC_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;
@@ -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_REC_LEN(1);
+ dir->rec_len = __EXT2_DIR_REC_LEN(1);
/*
* Set up entry for '..'
@@ -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)))
@@ -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 */