All of lore.kernel.org
 help / color / mirror / Atom feed
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
To: <ntfs3@lists.linux.dev>, LKML <linux-kernel@vger.kernel.org>,
	Linux-fsdevel <linux-fsdevel@vger.kernel.org>
Subject: [PATCH 09/11] fs/ntfs3: Optimize to store sorted attribute definition table
Date: Wed, 17 Apr 2024 16:08:25 +0300	[thread overview]
Message-ID: <a3158bb9-4ef6-482f-ad1c-b251a93f661a@paragon-software.com> (raw)
In-Reply-To: <6c99c1bd-448d-4301-8404-50df34e8df8e@paragon-software.com>

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(-)

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


  parent reply	other threads:[~2024-04-17 13:08 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-17 13:03 [PATCH 00/11] Bugfix and refactoring Konstantin Komarov
2024-04-17 13:04 ` [PATCH 01/11] fs/ntfs3: Remove max link count info display during driver init Konstantin Komarov
2024-04-17 13:04 ` [PATCH 02/11] fs/ntfs3: Missed le32_to_cpu conversion Konstantin Komarov
2024-04-17 13:06 ` [PATCH 03/11] fs/ntfs3: Mark volume as dirty if xattr is broken Konstantin Komarov
2024-04-17 13:06 ` [PATCH 04/11] fs/ntfs3: Use variable length array instead of fixed size Konstantin Komarov
2024-04-17 13:06 ` [PATCH 05/11] fs/ntfs3: Use 64 bit variable to avoid 32 bit overflow Konstantin Komarov
2024-04-17 13:07 ` [PATCH 06/11] fs/ntfs3: Redesign ntfs_create_inode to return error code instead of inode Konstantin Komarov
2024-04-17 13:07 ` [PATCH 07/11] fs/ntfs3: Check 'folio' pointer for NULL Konstantin Komarov
2024-04-17 13:08 ` [PATCH 08/11] fs/ntfs3: Always make file nonresident if fallocate (xfstest 438) Konstantin Komarov
2024-04-17 13:08 ` Konstantin Komarov [this message]
2024-04-17 13:09 ` [PATCH 10/11] fs/ntfs3: Remove cached label from sbi Konstantin Komarov
2024-04-22 20:42   ` Nathan Chancellor
2024-04-17 13:10 ` [PATCH 11/11] fs/ntfs3: Taking DOS names into account during link counting Konstantin Komarov
2024-04-18  6:31   ` Johan Hovold
2024-04-23  6:59     ` Konstantin Komarov
2024-04-18  6:42 ` [PATCH 00/11] Bugfix and refactoring Johan Hovold

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a3158bb9-4ef6-482f-ad1c-b251a93f661a@paragon-software.com \
    --to=almaz.alexandrovich@paragon-software.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ntfs3@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.