[PATCH 09/11] fs/ntfs3: Optimize to store sorted attribute definition table
From: Konstantin Komarov
Date: Wed Apr 17 2024 - 09:08:56 EST
0x18 bytes instead of 0xa0
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
---
fs/ntfs3/fsntfs.c | 60 ++++++++++++++++++++++++++++-
fs/ntfs3/inode.c | 30 +++++++--------
fs/ntfs3/ntfs.h | 4 --
fs/ntfs3/ntfs_fs.h | 40 ++++++++++---------
fs/ntfs3/super.c | 95 +++++++++++++++-------------------------------
fs/ntfs3/xattr.c | 6 +--
6 files changed, 126 insertions(+), 109 deletions(-)
diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c
index ae2ef5c11868..f9c60f3cadaf 100644
--- a/fs/ntfs3/fsntfs.c
+++ b/fs/ntfs3/fsntfs.c
@@ -2698,4 +2698,62 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8
*label, int len)
out:
__putname(uni);
return err;
-}
\ No newline at end of file
+}
+
+/*
+ * Check $AttrDef content and store sorted small $AttrDef entries
+ */
+int ntfs_check_attr_def(struct ntfs_sb_info *sbi,
+ const struct ATTR_DEF_ENTRY *raw, u32 bytes)
+{
+ const struct ATTR_DEF_ENTRY *de_s;
+ struct ATTR_DEF_ENTRY_SMALL *de_d;
+ u32 i, j;
+ u32 max_attr_type;
+ u32 n = (bytes / sizeof(*raw)) * sizeof(*raw);
+
+ for (i = 0, max_attr_type = 0, de_s = raw; i < n; i++, de_s++) {
+ u64 sz;
+ u32 attr_type = le32_to_cpu(de_s->type);
+
+ if (!attr_type)
+ break;
+
+ if ((attr_type & 0xf) || (!i && ATTR_STD != de_s->type) ||
+ (i && le32_to_cpu(de_s[-1].type) >= attr_type)) {
+ return -EINVAL;
+ }
+
+ max_attr_type = attr_type;
+
+ sz = le64_to_cpu(de_s->max_sz);
+ if (de_s->type == ATTR_REPARSE)
+ sbi->attrdef.rp_max_size = sz;
+ else if (de_s->type == ATTR_EA)
+ sbi->attrdef.ea_max_size = sz;
+ else if (de_s->type == ATTR_LABEL)
+ sbi->attrdef.label_max_size = sz;
+ }
+
+ /* Last known attribute type is 0x100. */
+ if (!max_attr_type || max_attr_type > 0x200)
+ return -EINVAL;
+
+ n = max_attr_type >> 4;
+ sbi->attrdef.table = kcalloc(n, sizeof(*de_d), GFP_KERNEL);
+ if (!sbi->attrdef.table)
+ return -ENOMEM;
+
+ for (j = 0, de_s = raw; j < i; j++, de_s++) {
+ u32 idx = (le32_to_cpu(de_s->type) >> 4) - 1;
+ de_d = sbi->attrdef.table + idx;
+
+ de_d->type = de_s->type;
+ de_d->flags = de_s->flags;
+ de_d->min_sz = le64_to_cpu(de_s->min_sz);
+ de_d->max_sz = le64_to_cpu(de_s->max_sz);
+ }
+ sbi->attrdef.entries = n;
+
+ return 0;
+}
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index 94177c1dd818..ae4465bf099f 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -18,7 +18,7 @@
#include "ntfs_fs.h"
/*
- * ntfs_read_mft - Read record and parses MFT.
+ * ntfs_read_mft - Read record and parse MFT.
*/
static struct inode *ntfs_read_mft(struct inode *inode,
const struct cpu_str *name,
@@ -1090,29 +1090,27 @@ int ntfs_flush_inodes(struct super_block *sb,
struct inode *i1,
return ret;
}
-int inode_write_data(struct inode *inode, const void *data, size_t bytes)
+/*
+ * Helper function to read file.
+ */
+int inode_read_data(struct inode *inode, void *data, size_t bytes)
{
pgoff_t idx;
+ struct address_space *mapping = inode->i_mapping;
- /* Write non resident data. */
for (idx = 0; bytes; idx++) {
size_t op = bytes > PAGE_SIZE ? PAGE_SIZE : bytes;
- struct page *page = ntfs_map_page(inode->i_mapping, idx);
+ struct page *page = read_mapping_page(mapping, idx, NULL);
+ void *kaddr;
if (IS_ERR(page))
return PTR_ERR(page);
- lock_page(page);
- WARN_ON(!PageUptodate(page));
- ClearPageUptodate(page);
-
- memcpy(page_address(page), data, op);
-
- flush_dcache_page(page);
- SetPageUptodate(page);
- unlock_page(page);
+ kaddr = kmap_atomic(page);
+ memcpy(data, kaddr, op);
+ kunmap_atomic(kaddr);
- ntfs_unmap_page(page);
+ put_page(page);
bytes -= op;
data = Add2Ptr(data, PAGE_SIZE);
@@ -1160,7 +1158,7 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info
*sbi, const char *symname,
/* err = the length of unicode name of symlink. */
*nsize = ntfs_reparse_bytes(err);
- if (*nsize > sbi->reparse.max_size) {
+ if (*nsize > sbi->attrdef.rp_max_size) {
err = -EFBIG;
goto out;
}
@@ -1954,7 +1952,7 @@ static noinline int ntfs_readlink_hlp(const struct
dentry *link_de,
rp = NULL;
}
- if (size > sbi->reparse.max_size || size <= sizeof(u32))
+ if (size > sbi->attrdef.rp_max_size || size <= sizeof(u32))
goto out;
if (!rp) {
diff --git a/fs/ntfs3/ntfs.h b/fs/ntfs3/ntfs.h
index 3d6143c7abc0..1dd03ba1dc93 100644
--- a/fs/ntfs3/ntfs.h
+++ b/fs/ntfs3/ntfs.h
@@ -817,7 +817,6 @@ struct VOLUME_INFO {
#define SIZEOF_ATTRIBUTE_VOLUME_INFO 0xc
-#define NTFS_LABEL_MAX_LENGTH (0x100 / sizeof(short))
#define NTFS_ATTR_INDEXABLE cpu_to_le32(0x00000002)
#define NTFS_ATTR_DUPALLOWED cpu_to_le32(0x00000004)
#define NTFS_ATTR_MUST_BE_INDEXED cpu_to_le32(0x00000010)
@@ -1002,9 +1001,6 @@ struct REPARSE_POINT {
static_assert(sizeof(struct REPARSE_POINT) == 0x18);
-/* Maximum allowed size of the reparse data. */
-#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
-
/*
* The value of the following constant needs to satisfy the following
* conditions:
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 00dec0ec5648..1d4fb6f87dea 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -201,6 +201,15 @@ struct ntfs_index {
u8 type; // index_mutex_classed
};
+/* NOT ondisk!. Just a small copy of $AttrDef file entry. */
+struct ATTR_DEF_ENTRY_SMALL {
+ enum ATTR_TYPE type;
+ __le32 flags;
+ u64 min_sz;
+ u64 max_sz;
+};
+static_assert(sizeof(struct ATTR_DEF_ENTRY_SMALL) == 0x18);
+
/* Minimum MFT zone. */
#define NTFS_MIN_MFT_ZONE 100
/* Step to increase the MFT. */
@@ -242,9 +251,13 @@ struct ntfs_sb_info {
CLST reparse_no;
CLST usn_jrnl_no;
- struct ATTR_DEF_ENTRY *def_table; // Attribute definition table.
- u32 def_entries;
- u32 ea_max_size;
+ struct {
+ u64 rp_max_size; // 16K
+ u32 entries;
+ u32 ea_max_size;
+ u32 label_max_size;
+ struct ATTR_DEF_ENTRY_SMALL *table; // 'entries'.
+ } attrdef;
struct MFT_REC *new_rec;
@@ -296,7 +309,6 @@ struct ntfs_sb_info {
struct {
struct ntfs_index index_r;
struct ntfs_inode *ni;
- u64 max_size; // 16K
} reparse;
struct {
@@ -658,6 +670,8 @@ int run_deallocate(struct ntfs_sb_info *sbi, const
struct runs_tree *run,
bool trim);
bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str
*name);
int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len);
+int ntfs_check_attr_def(struct ntfs_sb_info *sbi,
+ const struct ATTR_DEF_ENTRY *raw, u32 bytes);
/* Globals from index.c */
int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni,
size_t *bit);
@@ -714,7 +728,7 @@ int ntfs3_write_inode(struct inode *inode, struct
writeback_control *wbc);
int ntfs_sync_inode(struct inode *inode);
int ntfs_flush_inodes(struct super_block *sb, struct inode *i1,
struct inode *i2);
-int inode_write_data(struct inode *inode, const void *data, size_t bytes);
+int inode_read_data(struct inode *inode, void *data, size_t bytes);
int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const struct cpu_str *uni,
umode_t mode, dev_t dev, const char *symname, u32 size,
@@ -908,22 +922,6 @@ static inline bool ntfs_is_meta_file(struct
ntfs_sb_info *sbi, CLST rno)
rno == sbi->usn_jrnl_no;
}
-static inline void ntfs_unmap_page(struct page *page)
-{
- kunmap(page);
- put_page(page);
-}
-
-static inline struct page *ntfs_map_page(struct address_space *mapping,
- unsigned long index)
-{
- struct page *page = read_mapping_page(mapping, index, NULL);
-
- if (!IS_ERR(page))
- kmap(page);
- return page;
-}
-
static inline size_t wnd_zone_bit(const struct wnd_bitmap *wnd)
{
return wnd->zone_bit;
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index ac4722011140..8beefbca5769 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -624,7 +624,7 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)
{
kfree(sbi->new_rec);
kvfree(ntfs_put_shared(sbi->upcase));
- kvfree(sbi->def_table);
+ kfree(sbi->attrdef.table);
kfree(sbi->compress.lznt);
#ifdef CONFIG_NTFS3_LZX_XPRESS
xpress_free_decompressor(sbi->compress.xpress);
@@ -1157,8 +1157,6 @@ static int ntfs_fill_super(struct super_block *sb,
struct fs_context *fc)
CLST vcn, lcn, len;
struct ATTRIB *attr;
const struct VOLUME_INFO *info;
- u32 idx, done, bytes;
- struct ATTR_DEF_ENTRY *t;
u16 *shared;
struct MFT_REF ref;
bool ro = sb_rdonly(sb);
@@ -1199,7 +1197,7 @@ static int ntfs_fill_super(struct super_block *sb,
struct fs_context *fc)
/*
* Load $Volume. This should be done before $LogFile
- * 'cause 'sbi->volume.ni' is used 'ntfs_set_state'.
+ * 'cause 'sbi->volume.ni' is used in 'ntfs_set_state'.
*/
ref.low = cpu_to_le32(MFT_REC_VOL);
ref.seq = cpu_to_le16(MFT_REC_VOL);
@@ -1422,54 +1420,28 @@ static int ntfs_fill_super(struct super_block
*sb, struct fs_context *fc)
goto put_inode_out;
}
- bytes = inode->i_size;
- sbi->def_table = t = kvmalloc(bytes, GFP_KERNEL);
- if (!t) {
- err = -ENOMEM;
- goto put_inode_out;
- }
-
- for (done = idx = 0; done < bytes; done += PAGE_SIZE, idx++) {
- unsigned long tail = bytes - done;
- struct page *page = ntfs_map_page(inode->i_mapping, idx);
-
- if (IS_ERR(page)) {
- err = PTR_ERR(page);
- ntfs_err(sb, "Failed to read $AttrDef (%d).", err);
+ {
+ u32 bytes = inode->i_size;
+ struct ATTR_DEF_ENTRY *def_table = kmalloc(bytes, GFP_KERNEL);
+ if (!def_table) {
+ err = -ENOMEM;
goto put_inode_out;
}
- memcpy(Add2Ptr(t, done), page_address(page),
- min(PAGE_SIZE, tail));
- ntfs_unmap_page(page);
- if (!idx && ATTR_STD != t->type) {
- ntfs_err(sb, "$AttrDef is corrupted.");
- err = -EINVAL;
- goto put_inode_out;
+ /* Read the entire file. */
+ err = inode_read_data(inode, def_table, bytes);
+ if (err) {
+ ntfs_err(sb, "Failed to read $AttrDef (%d).", err);
+ } else {
+ /* Check content and store sorted array. */
+ err = ntfs_check_attr_def(sbi, def_table, bytes);
+ if (err)
+ ntfs_err(sb, "$AttrDef is corrupted.");
}
- }
-
- t += 1;
- sbi->def_entries = 1;
- done = sizeof(struct ATTR_DEF_ENTRY);
- sbi->reparse.max_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
- sbi->ea_max_size = 0x10000; /* default formatter value */
-
- while (done + sizeof(struct ATTR_DEF_ENTRY) <= bytes) {
- u32 t32 = le32_to_cpu(t->type);
- u64 sz = le64_to_cpu(t->max_sz);
-
- if ((t32 & 0xF) || le32_to_cpu(t[-1].type) >= t32)
- break;
-
- if (t->type == ATTR_REPARSE)
- sbi->reparse.max_size = sz;
- else if (t->type == ATTR_EA)
- sbi->ea_max_size = sz;
- done += sizeof(struct ATTR_DEF_ENTRY);
- t += 1;
- sbi->def_entries += 1;
+ kfree(def_table);
+ if (err)
+ goto put_inode_out;
}
iput(inode);
@@ -1489,27 +1461,22 @@ static int ntfs_fill_super(struct super_block
*sb, struct fs_context *fc)
goto put_inode_out;
}
- for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) {
- const __le16 *src;
- u16 *dst = Add2Ptr(sbi->upcase, idx << PAGE_SHIFT);
- struct page *page = ntfs_map_page(inode->i_mapping, idx);
-
- if (IS_ERR(page)) {
- err = PTR_ERR(page);
- ntfs_err(sb, "Failed to read $UpCase (%d).", err);
- goto put_inode_out;
- }
-
- src = page_address(page);
+ /* Read the entire file. */
+ err = inode_read_data(inode, sbi->upcase, 0x10000 * sizeof(short));
+ if (err) {
+ ntfs_err(sb, "Failed to read $UpCase (%d).", err);
+ goto put_inode_out;
+ }
#ifdef __BIG_ENDIAN
- for (i = 0; i < PAGE_SIZE / sizeof(u16); i++)
+ {
+ const __le16 *src = sbi->upcase;
+ u16 *dst = sbi->upcase;
+
+ for (i = 0; i < 0x10000; i++)
*dst++ = le16_to_cpu(*src++);
-#else
- memcpy(dst, src, PAGE_SIZE);
-#endif
- ntfs_unmap_page(page);
}
+#endif
shared = ntfs_set_shared(sbi->upcase, 0x10000 * sizeof(short));
if (shared && sbi->upcase != shared) {
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index 872df2197202..a7f122e51c04 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -99,12 +99,12 @@ static int ntfs_read_ea(struct ntfs_inode *ni,
struct EA_FULL **ea,
/* Check Ea limit. */
size = le32_to_cpu((*info)->size);
- if (size > sbi->ea_max_size) {
+ if (size > sbi->attrdef.ea_max_size) {
err = -EFBIG;
goto out;
}
- if (attr_size(attr_ea) > sbi->ea_max_size) {
+ if (attr_size(attr_ea) > sbi->attrdef.ea_max_size) {
err = -EFBIG;
goto out;
}
@@ -430,7 +430,7 @@ static noinline int ntfs_set_ea(struct inode *inode,
const char *name,
* 1. Check ea_info.size_pack for overflow.
* 2. New attribute size must fit value from $AttrDef
*/
- if (new_pack > 0xffff || size > sbi->ea_max_size) {
+ if (new_pack > 0xffff || size > sbi->attrdef.ea_max_size) {
ntfs_inode_warn(
inode,
"The size of extended attributes must not exceed 64KiB");
--
2.34.1