diff mbox series

[09/11] fs/ntfs3: Optimize to store sorted attribute definition table

Message ID a3158bb9-4ef6-482f-ad1c-b251a93f661a@paragon-software.com (mailing list archive)
State New, archived
Headers show
Series Bugfix and refactoring | expand

Commit Message

Konstantin Komarov April 17, 2024, 1:08 p.m. UTC
0x18 bytes instead of 0xa0

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
---
  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(-)

      }
@@ -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");
diff mbox series

Patch

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;