Viewing: ext4-dir-entry-len.patch
Index: linux-stage/fs/ext4/ext4.h
===================================================================
--- linux-stage.orig/fs/ext4/ext4.h
+++ linux-stage/fs/ext4/ext4.h
@@ -1191,6 +1191,7 @@ struct ext4_inode_info {
* Mount flags set via mount options or defaults
*/
#define EXT4_MOUNT_NO_MBCACHE 0x00001 /* Do not use mbcache */
+#define EXT4_MOUNT_DIRDATA 0x00002 /* Data in directory entries */
#define EXT4_MOUNT_GRPID 0x00004 /* Create files with directory's group */
#define EXT4_MOUNT_DEBUG 0x00008 /* Some debugging messages */
#define EXT4_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */
@@ -2193,6 +2194,7 @@ static inline bool ext4_has_feature_encr
EXT4_FEATURE_INCOMPAT_FLEX_BG| \
EXT4_FEATURE_INCOMPAT_EA_INODE| \
EXT4_FEATURE_INCOMPAT_MMP | \
+ EXT4_FEATURE_INCOMPAT_DIRDATA | \
EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
EXT4_FEATURE_INCOMPAT_ENCRYPT | \
EXT4_FEATURE_INCOMPAT_CASEFOLD | \
@@ -2404,6 +2406,35 @@ struct ext4_dir_entry_tail {
#define EXT4_FT_SYMLINK 7
#define EXT4_FT_MAX 8
+#define EXT4_FT_MASK 0xf
+
+#if EXT4_FT_MAX > EXT4_FT_MASK
+#error "conflicting EXT4_FT_MAX and EXT4_FT_MASK"
+#endif
+
+/*
+ * d_type has 4 unused bits, so it can hold four types data. these different
+ * type of data (e.g. lustre data, high 32 bits of 64-bit inode number) can be
+ * stored, in flag order, after file-name in ext4 dirent.
+*/
+/*
+ * These flags is added to d_type if ext4 dirent has extra data after
+ * filename. This data length is variable and length is stored in first byte
+ * of data. Data starts after filename NUL byte.
+ */
+#define EXT4_DIRENT_LUFID 0x10
+#define EXT4_DIRENT_INO64 0x20
+#define EXT4_DIRENT_CFHASH 0x40
+
+struct ext4_dirent_data_header {
+ /* length of this header + the whole data blob */
+ __u8 ddh_length;
+} __packed;
+
+struct ext4_dirent_hash {
+ struct ext4_dirent_data_header dh_header;
+ struct ext4_dir_entry_hash dh_hash;
+} __packed;
#define EXT4_FT_DIR_CSUM 0xDE
@@ -2416,16 +2447,28 @@ struct ext4_dir_entry_tail {
#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
#define EXT4_MAX_REC_LEN ((1<<16)-1)
+#define EXT4_DIR_REC_LEN_(name_len, i_dir) \
+ ext4_dir_rec_len((name_len), (i_dir))
+#define EXT4_DIR_ENTRY_LEN_(de, i_dir) \
+ (EXT4_DIR_REC_LEN_((de)->name_len + ext4_get_dirent_data_len(de), \
+ (i_dir)))
+/* ldiskfs */
+#define EXT4_DIR_REC_LEN(name_len, i_dir) EXT4_DIR_REC_LEN_((name_len), (i_dir))
+#define EXT4_DIR_ENTRY_LEN(de, i_dir) EXT4_DIR_ENTRY_LEN_((de), (i_dir))
+/* lustre osd_handler compat -- ifdef LDISKFS_DIR_REC_LEN_WITH_DIR */
+#define EXT4_DIR_REC_LEN_WITH_DIR 1
+#define __EXT4_DIR_REC_LEN(name_len) EXT4_DIR_REC_LEN_((name_len), NULL)
+
/*
* The rec_len is dependent on the type of directory. Directories that are
* casefolded and encrypted need to store the hash as well, so we add room for
* ext4_extended_dir_entry_2. For all entries related to '.' or '..' you should
* pass NULL for dir, as those entries do not use the extra fields.
*/
-static inline unsigned int ext4_dir_rec_len(__u8 name_len,
+static inline unsigned int ext4_dir_rec_len(__u32 name_len,
const struct inode *dir)
{
- int rec_len = (name_len + 8 + EXT4_DIR_ROUND);
+ __u32 rec_len = (name_len + 8 + EXT4_DIR_ROUND);
if (dir && ext4_hash_in_dirent(dir))
rec_len += sizeof(struct ext4_dir_entry_hash);
@@ -2879,10 +2922,16 @@ static const unsigned char ext4_filetype
static inline unsigned char get_dtype(struct super_block *sb, int filetype)
{
- if (!ext4_has_feature_filetype(sb) || filetype >= EXT4_FT_MAX)
+ int fl_index = filetype & EXT4_FT_MASK;
+
+ if (!ext4_has_feature_filetype(sb) || fl_index >= EXT4_FT_MAX)
return DT_UNKNOWN;
- return ext4_filetype_table[filetype];
+ if (!test_opt(sb, DIRDATA))
+ return ext4_filetype_table[fl_index];
+
+ return (ext4_filetype_table[fl_index]) |
+ (filetype & ~EXT4_FT_MASK);
}
extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh,
void *buf, int buf_size);
@@ -3879,6 +3928,48 @@ static inline void ext4_clear_io_unwritt
}
}
+#define ext4_dirdata_next(ddh) \
+ (struct ext4_dirent_data_header *)((char *)ddh + ddh->ddh_length)
+/*
+ * 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.
+ */
+static inline int ext4_get_dirent_data_len(struct ext4_dir_entry_2 *de)
+{
+ struct ext4_dirent_data_header *ddh =
+ (struct ext4_dirent_data_header *)de->name + de->name_len +
+ 1 /* NUL terminator */;
+ __u8 extra_data_flags = (de->file_type & ~EXT4_FT_MASK) >> 4;
+ struct ext4_dir_entry_tail *t = (struct ext4_dir_entry_tail *)de;
+ int dlen = 0;
+
+ if (!t->det_reserved_zero1 &&
+ le16_to_cpu(t->det_rec_len) ==
+ sizeof(struct ext4_dir_entry_tail) &&
+ !t->det_reserved_zero2 &&
+ t->det_reserved_ft == EXT4_FT_DIR_CSUM)
+ return 0;
+
+ while (extra_data_flags) {
+ if (extra_data_flags & 1) {
+ dlen += ddh->ddh_length + (dlen == 0);
+ ddh = ext4_dirdata_next(ddh);
+ }
+ extra_data_flags >>= 1;
+ }
+ return dlen;
+}
+
+/* Inline function to calculate directory entry length */
+static inline unsigned int ext4_dir_entry_len(struct ext4_dir_entry_2 *de,
+ const struct inode *dir)
+{
+ unsigned int name_len = de->name_len + ext4_get_dirent_data_len(de);
+ return ext4_dir_rec_len(name_len, dir);
+}
+
extern const struct iomap_ops ext4_iomap_ops;
extern const struct iomap_ops ext4_iomap_overwrite_ops;
extern const struct iomap_ops ext4_iomap_report_ops;
Index: linux-stage/fs/ext4/namei.c
===================================================================
--- linux-stage.orig/fs/ext4/namei.c
+++ linux-stage/fs/ext4/namei.c
@@ -557,13 +557,15 @@ ext4_next_entry(struct ext4_dir_entry_2
* Future: use high four bits of block for coalesce-on-delete flags
* Mask them off for now.
*/
-struct dx_root_info *dx_get_dx_info(struct ext4_dir_entry_2 *de)
+static struct dx_root_info *dx_get_dx_info(struct ext4_dir_entry_2 *de)
{
/* get dotdot first */
- de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(1));
+ de = (struct ext4_dir_entry_2 *)((char *)de +
+ ext4_dir_entry_len(de, NULL));
/* dx root info is after dotdot entry */
- de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(2));
+ de = (struct ext4_dir_entry_2 *)((char *)de +
+ ext4_dir_entry_len(de, NULL));
return (struct dx_root_info *)de;
}
@@ -2384,7 +2386,7 @@ out_frames:
return retval;
}
-/* update ".." entry */
+/* update ".." entry, try to expand the entry if necessary */
static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
struct inode *inode)
{
@@ -3062,7 +3064,7 @@ struct ext4_dir_entry_2 *ext4_init_dot_d
blocksize);
else
de->rec_len = ext4_rec_len_to_disk(
- ext4_dir_rec_len(de->name_len, NULL),
+ ext4_dir_rec_len(de, NULL),
blocksize);
strcpy(de->name, "..");
ext4_set_de_type(inode->i_sb, de, S_IFDIR);