|
@@ -19,7 +19,8 @@
|
|
|
|
|
|
#define EXT4_XATTR_SYSTEM_DATA "data"
|
|
|
#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS))
|
|
|
-#define EXT4_INLINE_DOTDOT_SIZE 4
|
|
|
+#define EXT4_INLINE_DOTDOT_OFFSET 2
|
|
|
+#define EXT4_INLINE_DOTDOT_SIZE 4
|
|
|
|
|
|
int ext4_get_inline_size(struct inode *inode)
|
|
|
{
|
|
@@ -1289,6 +1290,112 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This function fills a red-black tree with information from an
|
|
|
+ * inlined dir. It returns the number directory entries loaded
|
|
|
+ * into the tree. If there is an error it is returned in err.
|
|
|
+ */
|
|
|
+int htree_inlinedir_to_tree(struct file *dir_file,
|
|
|
+ struct inode *dir, ext4_lblk_t block,
|
|
|
+ struct dx_hash_info *hinfo,
|
|
|
+ __u32 start_hash, __u32 start_minor_hash,
|
|
|
+ int *has_inline_data)
|
|
|
+{
|
|
|
+ int err = 0, count = 0;
|
|
|
+ unsigned int parent_ino;
|
|
|
+ int pos;
|
|
|
+ struct ext4_dir_entry_2 *de;
|
|
|
+ struct inode *inode = file_inode(dir_file);
|
|
|
+ int ret, inline_size = 0;
|
|
|
+ struct ext4_iloc iloc;
|
|
|
+ void *dir_buf = NULL;
|
|
|
+ struct ext4_dir_entry_2 fake;
|
|
|
+
|
|
|
+ ret = ext4_get_inode_loc(inode, &iloc);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ down_read(&EXT4_I(inode)->xattr_sem);
|
|
|
+ if (!ext4_has_inline_data(inode)) {
|
|
|
+ up_read(&EXT4_I(inode)->xattr_sem);
|
|
|
+ *has_inline_data = 0;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline_size = ext4_get_inline_size(inode);
|
|
|
+ dir_buf = kmalloc(inline_size, GFP_NOFS);
|
|
|
+ if (!dir_buf) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ up_read(&EXT4_I(inode)->xattr_sem);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ext4_read_inline_data(inode, dir_buf, inline_size, &iloc);
|
|
|
+ up_read(&EXT4_I(inode)->xattr_sem);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pos = 0;
|
|
|
+ parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode);
|
|
|
+ while (pos < inline_size) {
|
|
|
+ /*
|
|
|
+ * As inlined dir doesn't store any information about '.' and
|
|
|
+ * only the inode number of '..' is stored, we have to handle
|
|
|
+ * them differently.
|
|
|
+ */
|
|
|
+ if (pos == 0) {
|
|
|
+ fake.inode = cpu_to_le32(inode->i_ino);
|
|
|
+ fake.name_len = 1;
|
|
|
+ strcpy(fake.name, ".");
|
|
|
+ fake.rec_len = ext4_rec_len_to_disk(
|
|
|
+ EXT4_DIR_REC_LEN(fake.name_len),
|
|
|
+ inline_size);
|
|
|
+ ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
|
|
|
+ de = &fake;
|
|
|
+ pos = EXT4_INLINE_DOTDOT_OFFSET;
|
|
|
+ } else if (pos == EXT4_INLINE_DOTDOT_OFFSET) {
|
|
|
+ fake.inode = cpu_to_le32(parent_ino);
|
|
|
+ fake.name_len = 2;
|
|
|
+ strcpy(fake.name, "..");
|
|
|
+ fake.rec_len = ext4_rec_len_to_disk(
|
|
|
+ EXT4_DIR_REC_LEN(fake.name_len),
|
|
|
+ inline_size);
|
|
|
+ ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
|
|
|
+ de = &fake;
|
|
|
+ pos = EXT4_INLINE_DOTDOT_SIZE;
|
|
|
+ } else {
|
|
|
+ de = (struct ext4_dir_entry_2 *)(dir_buf + pos);
|
|
|
+ pos += ext4_rec_len_from_disk(de->rec_len, inline_size);
|
|
|
+ if (ext4_check_dir_entry(inode, dir_file, de,
|
|
|
+ iloc.bh, dir_buf,
|
|
|
+ inline_size, pos)) {
|
|
|
+ ret = count;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ext4fs_dirhash(de->name, de->name_len, hinfo);
|
|
|
+ if ((hinfo->hash < start_hash) ||
|
|
|
+ ((hinfo->hash == start_hash) &&
|
|
|
+ (hinfo->minor_hash < start_minor_hash)))
|
|
|
+ continue;
|
|
|
+ if (de->inode == 0)
|
|
|
+ continue;
|
|
|
+ err = ext4_htree_store_dirent(dir_file,
|
|
|
+ hinfo->hash, hinfo->minor_hash, de);
|
|
|
+ if (err) {
|
|
|
+ count = err;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+ ret = count;
|
|
|
+out:
|
|
|
+ kfree(dir_buf);
|
|
|
+ brelse(iloc.bh);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
int ext4_read_inline_dir(struct file *filp,
|
|
|
void *dirent, filldir_t filldir,
|
|
|
int *has_inline_data)
|