[PATCH 0/3] ntfs: fix mount failure cleanup and free-count setup

DaeMyung Kang posted 3 patches 4 days, 4 hours ago
There is a newer version of this series
super.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
[PATCH 0/3] ntfs: fix mount failure cleanup and free-count setup
Posted by DaeMyung Kang 4 days, 4 hours ago
Hi Namjae,

This small series is based on current ntfs-next commit d8756162c6ca
("ntfs: remove unsupported quota handling").

The previous quota_ino reference leak fix is intentionally dropped
because that commit removes load_and_init_quota(), the $Quota/$Q state,
and the quota out-of-date marker path.

The remaining fixes cover mount failure cleanup, a free-cluster precalc
OOM path that can otherwise leave waiters blocked forever, and the
$UpCase default-table alias check.  The free-cluster patch also clears
the per-bitmap-page free counters on the conservative zero-free-count
path so later allocation heuristics stay conservative after the known
flag is published.

Testing:
- For patch 1, I built a DEBUG_KMEMLEAK/DEBUG_FS kernel and injected an
  ntfs_fill_super() failure immediately after load_system_files().  Before
  the fix, kmemleak reported the leaked volume label allocated from
  ntfs_ucstonls(); after the fix, the same failure path produced no
  kmemleak report.
- For patch 2, I used QEMU with a debug injection that forces the initial
  precalc readahead-state allocation to fail.  Before the fix, statfs
  remained blocked in D state in ntfs_statfs(); after the fix, the waiter
  woke up and statfs returned with the conservative zero free-cluster
  count.

DaeMyung Kang (3):
  ntfs: free volume-wide resources on fill_super failure
  ntfs: wake free-cluster waiters when precalc allocation fails
  ntfs: only alias volume $UpCase to default on exact match

 super.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)


base-commit: d8756162c6cab1ad4478539d33da6cab413af1ee
-- 
2.43.0
[PATCH v3 0/3] ntfs: fix mount failure cleanup and free-count setup
Posted by DaeMyung Kang 3 days, 8 hours ago
Hi Namjae,

This v3 is based on current mainline at 8bc67e4db64a.  The patches
modify fs/ntfs/super.c in the Linux kernel tree.

The fixes cover mount failure cleanup, a free-cluster precalc allocation
failure path that can otherwise leave waiters blocked forever, and the
$UpCase default-table alias check.  Per review, the free-cluster patch
now avoids the temporary file_ra_state heap allocation entirely by using
a stack variable for the synchronous bitmap scan.

Testing:
- Built fs/ntfs/super.o and fs/ntfs/ntfs.ko with gcc 13.3.0.
- For patch 1, I built a DEBUG_KMEMLEAK/DEBUG_FS kernel and injected an
  ntfs_fill_super() failure immediately after load_system_files().  Before
  the fix, kmemleak reported the leaked volume label allocated from
  ntfs_ucstonls(); after the fix, the same failure path produced no
  kmemleak report.
- For patch 2, I used QEMU with a debug injection that forces the initial
  precalc readahead-state allocation to fail.  Before the fix, statfs
  remained blocked in D state in ntfs_statfs().  This version removes
  that heap allocation and therefore removes the injected failure point.
  I also mounted a fresh NTFS image with the stack-backed state and
  verified statfs reported the same free count as ntfsinfo -m.

Changes since v2:
- Changed patch 2's on-stack file_ra_state initializer from the empty
  initializer to { 0 } to avoid build failures with stricter compilers.
- Re-tested patch 2 alone and patches 1-2 on a clean mainline tree.

Changes since v1:
- Rebased and regenerated the series for the Linux kernel tree
  (fs/ntfs/super.c), instead of the Github out-of-tree layout.
- Changed patch 2 to declare the temporary file_ra_state on the stack,
  as suggested by Hyunchul Lee, instead of adding an OOM fallback path.
- Zero-initialized patch 2's on-stack file_ra_state because
  file_ra_state_init() only initializes ra_pages and prev_pos.
- Reworded patch 3 to avoid overstating user-visible name comparison
  effects, and used memcmp() for the exact $UpCase table comparison.

DaeMyung Kang (3):
  ntfs: free volume-wide resources on fill_super failure
  ntfs: avoid heap allocation for free-cluster readahead state
  ntfs: only alias volume $UpCase to default on exact match

 fs/ntfs/super.c | 28 +++++++++-------------------
 1 file changed, 9 insertions(+), 19 deletions(-)


base-commit: 8bc67e4db64aa72732c474b44ea8622062c903f0
-- 
2.43.0
Re: [PATCH v3 0/3] ntfs: fix mount failure cleanup and free-count setup
Posted by Namjae Jeon 1 day, 7 hours ago
On Thu, May 21, 2026 at 10:00 PM DaeMyung Kang <charsyam@gmail.com> wrote:
>
> Hi Namjae,
>
> This v3 is based on current mainline at 8bc67e4db64a.  The patches
> modify fs/ntfs/super.c in the Linux kernel tree.
>
> The fixes cover mount failure cleanup, a free-cluster precalc allocation
> failure path that can otherwise leave waiters blocked forever, and the
> $UpCase default-table alias check.  Per review, the free-cluster patch
> now avoids the temporary file_ra_state heap allocation entirely by using
> a stack variable for the synchronous bitmap scan.
>
> Testing:
> - Built fs/ntfs/super.o and fs/ntfs/ntfs.ko with gcc 13.3.0.
> - For patch 1, I built a DEBUG_KMEMLEAK/DEBUG_FS kernel and injected an
>   ntfs_fill_super() failure immediately after load_system_files().  Before
>   the fix, kmemleak reported the leaked volume label allocated from
>   ntfs_ucstonls(); after the fix, the same failure path produced no
>   kmemleak report.
> - For patch 2, I used QEMU with a debug injection that forces the initial
>   precalc readahead-state allocation to fail.  Before the fix, statfs
>   remained blocked in D state in ntfs_statfs().  This version removes
>   that heap allocation and therefore removes the injected failure point.
>   I also mounted a fresh NTFS image with the stack-backed state and
>   verified statfs reported the same free count as ntfsinfo -m.
>
> Changes since v2:
> - Changed patch 2's on-stack file_ra_state initializer from the empty
>   initializer to { 0 } to avoid build failures with stricter compilers.
> - Re-tested patch 2 alone and patches 1-2 on a clean mainline tree.
>
> Changes since v1:
> - Rebased and regenerated the series for the Linux kernel tree
>   (fs/ntfs/super.c), instead of the Github out-of-tree layout.
> - Changed patch 2 to declare the temporary file_ra_state on the stack,
>   as suggested by Hyunchul Lee, instead of adding an OOM fallback path.
> - Zero-initialized patch 2's on-stack file_ra_state because
>   file_ra_state_init() only initializes ra_pages and prev_pos.
> - Reworded patch 3 to avoid overstating user-visible name comparison
>   effects, and used memcmp() for the exact $UpCase table comparison.
>
> DaeMyung Kang (3):
>   ntfs: free volume-wide resources on fill_super failure
>   ntfs: avoid heap allocation for free-cluster readahead state
>   ntfs: only alias volume $UpCase to default on exact match
Applied them to #ntfs-next.
Thanks!
[PATCH v3 1/3] ntfs: free volume-wide resources on fill_super failure
Posted by DaeMyung Kang 3 days, 8 hours ago
ntfs_fill_super()'s err_out_now path frees only the volume struct via
kfree(vol), leaving several vol-owned allocations behind on every mount
failure:

  - vol->nls_map, loaded by ntfs_init_fs_context() via
    load_nls_default() (or replaced by an explicit nls= option in
    ntfs_parse_param()), is never unload_nls()'d.

  - vol->volume_label, allocated by load_system_files() through
    ntfs_ucstonls() once the $Volume name attribute has been parsed, is
    not released by load_system_files()'s own error labels nor by the
    fill_super() inline cleanup that only runs on d_make_root()
    failure.  Any later failure inside load_system_files() leaks it.

  - vol->lcn_empty_bits_per_page was kvfree()'d in
    unl_upcase_iput_tmp_ino_err_out_now without clearing the pointer,
    so it could not be folded into a single common cleanup.

Because the failure paths never call ntfs_volume_free() and never reach
the d_make_root() inline cleanup block (it sits above the label and is
jumped over by the load_system_files() / kvmalloc failure gotos), these
resources accumulate per failed mount attempt with no chance of
recovery short of unloading the module.  This is a silent leak: the
inodes loaded prior to failure remain hashed but generic_shutdown_super()
skips evict_inodes() when sb->s_root is unset, so no CHECK_DATA_CORRUPTION
warning is emitted either.

Move the per-volume frees down to err_out_now and drop the
lcn_empty_bits_per_page kvfree() from the upper label so the cleanup is
performed exactly once on every failure path.  Using unconditional
kvfree() / kfree() / unload_nls() is safe because they all accept NULL
and the upper labels that previously freed nls_map (the d_make_root()
inline cleanup) already clear the pointer.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 9e321cc2febe..7e3561265b47 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -2530,8 +2530,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	}
 	/* Error exit code path. */
 unl_upcase_iput_tmp_ino_err_out_now:
-	if (vol->lcn_empty_bits_per_page)
-		kvfree(vol->lcn_empty_bits_per_page);
 	/*
 	 * Decrease the number of upcase users and destroy the global default
 	 * upcase table if necessary.
@@ -2551,6 +2549,9 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	/* Errors at this stage are irrelevant. */
 err_out_now:
 	sb->s_fs_info = NULL;
+	kvfree(vol->lcn_empty_bits_per_page);
+	kfree(vol->volume_label);
+	unload_nls(vol->nls_map);
 	kfree(vol);
 	ntfs_debug("Failed, returning -EINVAL.");
 	lockdep_on();
-- 
2.43.0
[PATCH v3 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by DaeMyung Kang 3 days, 8 hours ago
get_nr_free_clusters() is run from the precalc_free_clusters() worker
queued at the end of ntfs_fill_super().  It is also the only place that
publishes the result by atomic64_set(&vol->free_clusters, ...), sets
NVolSetFreeClusterKnown(), and wakes vol->free_waitq.

The function currently allocates a temporary file_ra_state with
kzalloc() before that publication happens.  If the allocation fails,
get_nr_free_clusters() returns 0 without setting NVolFreeClusterKnown()
or waking vol->free_waitq, so callers that wait for the free count can
block forever.

The readahead state is only used synchronously while scanning the bitmap
and struct file_ra_state is small.  Keep it on the stack and pass it to
the readahead helper by address, eliminating the early allocation
failure path.  Zero-initialize the on-stack state because
file_ra_state_init() only sets ra_pages and prev_pos.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 7e3561265b47..d2c4fb6ea0d4 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1954,7 +1954,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
 	struct folio *folio;
 	pgoff_t index, max_index;
-	struct file_ra_state *ra;
+	struct file_ra_state ra = { 0 };
 
 	ntfs_debug("Entering.");
 	/* Serialize accesses to the cluster bitmap. */
@@ -1962,11 +1962,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	if (NVolFreeClusterKnown(vol))
 		return atomic64_read(&vol->free_clusters);
 
-	ra = kzalloc(sizeof(*ra), GFP_NOFS);
-	if (!ra)
-		return 0;
-
-	file_ra_state_init(ra, mapping);
+	file_ra_state_init(&ra, mapping);
 
 	/*
 	 * Convert the number of bits into bytes rounded up, then convert into
@@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 		 * Get folio from page cache, getting it from backing store
 		 * if necessary, and increment the use count.
 		 */
-		folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
+		folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
 
 		/* Ignore pages which errored synchronously. */
 		if (IS_ERR(folio)) {
@@ -2024,7 +2020,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	else
 		atomic64_set(&vol->free_clusters, nr_free);
 
-	kfree(ra);
 	NVolSetFreeClusterKnown(vol);
 	wake_up_all(&vol->free_waitq);
 	ntfs_debug("Exiting.");
-- 
2.43.0
[PATCH v3 3/3] ntfs: only alias volume $UpCase to default on exact match
Posted by DaeMyung Kang 3 days, 8 hours ago
load_and_init_upcase() currently aliases vol->upcase to the global
default upcase whenever the shared prefix matches, and then truncates
vol->upcase_len to that shorter prefix.  The result is correct only by
accident: upcase[] accesses in name collation are gated by upcase_len,
so the prefix-equality alias produces the same fold output as keeping
the volume's own shorter table.

Still, prefix equality is not equality: the volume table is logically
distinct from the default and should not be replaced by it unless they
are byte-for-byte identical.  Use memcmp() to compare the complete table
in one expression and drop the now-redundant upcase_len rewrite.

No user-visible change is expected for compliant volumes whose $UpCase
has exactly default_upcase_len entries; shorter volume tables are no
longer aliased to the default.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index d2c4fb6ea0d4..876e978370ba 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1323,7 +1323,6 @@ static bool load_and_init_upcase(struct ntfs_volume *vol)
 	u8 *addr;
 	pgoff_t index, max_index;
 	unsigned int size;
-	int i, max;
 
 	ntfs_debug("Entering.");
 	/* Read upcase table and setup vol->upcase and vol->upcase_len. */
@@ -1374,16 +1373,11 @@ static bool load_and_init_upcase(struct ntfs_volume *vol)
 		mutex_unlock(&ntfs_lock);
 		return true;
 	}
-	max = default_upcase_len;
-	if (max > vol->upcase_len)
-		max = vol->upcase_len;
-	for (i = 0; i < max; i++)
-		if (vol->upcase[i] != default_upcase[i])
-			break;
-	if (i == max) {
+	if (default_upcase_len == vol->upcase_len &&
+	    !memcmp(vol->upcase, default_upcase,
+		    default_upcase_len * sizeof(*default_upcase))) {
 		kvfree(vol->upcase);
 		vol->upcase = default_upcase;
-		vol->upcase_len = max;
 		ntfs_nr_upcase_users++;
 		mutex_unlock(&ntfs_lock);
 		ntfs_debug("Volume specified $UpCase matches default. Using default.");
-- 
2.43.0
[PATCH v2 0/3] ntfs: fix mount failure cleanup and free-count setup
Posted by DaeMyung Kang 3 days, 11 hours ago
Hi Namjae,

This v2 is ported to the Linux kernel tree and is based on current
mainline commit 8bc67e4db64a ("Merge tag 'erofs-for-7.1-rc5-fixes' of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs").  The
patches now modify fs/ntfs/super.c rather than the out-of-tree Github
layout.

The fixes cover mount failure cleanup, a free-cluster precalc allocation
failure path that can otherwise leave waiters blocked forever, and the
$UpCase default-table alias check.  Per review, the free-cluster patch
now avoids the temporary file_ra_state heap allocation entirely by using
a stack variable for the synchronous bitmap scan.

Testing:
- For patch 1, I built a DEBUG_KMEMLEAK/DEBUG_FS kernel and injected an
  ntfs_fill_super() failure immediately after load_system_files().  Before
  the fix, kmemleak reported the leaked volume label allocated from
  ntfs_ucstonls(); after the fix, the same failure path produced no
  kmemleak report.
- For patch 2, I used QEMU with a debug injection that forces the initial
  precalc readahead-state allocation to fail.  Before the fix, statfs
  remained blocked in D state in ntfs_statfs().  This version removes
  that heap allocation and therefore removes the injected failure point.
  I also mounted a fresh NTFS image with the stack-backed state and
  verified statfs reported the same free count as ntfsinfo -m.

Changes since v1:
- Rebased and regenerated the series for the Linux kernel tree
  (fs/ntfs/super.c), instead of the Github out-of-tree layout.
- Changed patch 2 to declare the temporary file_ra_state on the stack,
  as suggested by Hyunchul Lee, instead of adding an OOM fallback path.
- Zero-initialized patch 2's on-stack file_ra_state because
  file_ra_state_init() only initializes ra_pages and prev_pos.
- Reworded patch 3 to avoid overstating user-visible name comparison
  effects, and used memcmp() for the exact $UpCase table comparison.

DaeMyung Kang (3):
  ntfs: free volume-wide resources on fill_super failure
  ntfs: avoid heap allocation for free-cluster readahead state
  ntfs: only alias volume $UpCase to default on exact match

 fs/ntfs/super.c | 28 +++++++++-------------------
 1 file changed, 9 insertions(+), 19 deletions(-)


base-commit: 8bc67e4db64aa72732c474b44ea8622062c903f0
-- 
2.43.0
[PATCH v2 1/3] ntfs: free volume-wide resources on fill_super failure
Posted by DaeMyung Kang 3 days, 11 hours ago
ntfs_fill_super()'s err_out_now path frees only the volume struct via
kfree(vol), leaving several vol-owned allocations behind on every mount
failure:

  - vol->nls_map, loaded by ntfs_init_fs_context() via
    load_nls_default() (or replaced by an explicit nls= option in
    ntfs_parse_param()), is never unload_nls()'d.

  - vol->volume_label, allocated by load_system_files() through
    ntfs_ucstonls() once the $Volume name attribute has been parsed, is
    not released by load_system_files()'s own error labels nor by the
    fill_super() inline cleanup that only runs on d_make_root()
    failure.  Any later failure inside load_system_files() leaks it.

  - vol->lcn_empty_bits_per_page was kvfree()'d in
    unl_upcase_iput_tmp_ino_err_out_now without clearing the pointer,
    so it could not be folded into a single common cleanup.

Because the failure paths never call ntfs_volume_free() and never reach
the d_make_root() inline cleanup block (it sits above the label and is
jumped over by the load_system_files() / kvmalloc failure gotos), these
resources accumulate per failed mount attempt with no chance of
recovery short of unloading the module.  This is a silent leak: the
inodes loaded prior to failure remain hashed but generic_shutdown_super()
skips evict_inodes() when sb->s_root is unset, so no CHECK_DATA_CORRUPTION
warning is emitted either.

Move the per-volume frees down to err_out_now and drop the
lcn_empty_bits_per_page kvfree() from the upper label so the cleanup is
performed exactly once on every failure path.  Using unconditional
kvfree() / kfree() / unload_nls() is safe because they all accept NULL
and the upper labels that previously freed nls_map (the d_make_root()
inline cleanup) already clear the pointer.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 9e321cc2febe..7e3561265b47 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -2530,8 +2530,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	}
 	/* Error exit code path. */
 unl_upcase_iput_tmp_ino_err_out_now:
-	if (vol->lcn_empty_bits_per_page)
-		kvfree(vol->lcn_empty_bits_per_page);
 	/*
 	 * Decrease the number of upcase users and destroy the global default
 	 * upcase table if necessary.
@@ -2551,6 +2549,9 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	/* Errors at this stage are irrelevant. */
 err_out_now:
 	sb->s_fs_info = NULL;
+	kvfree(vol->lcn_empty_bits_per_page);
+	kfree(vol->volume_label);
+	unload_nls(vol->nls_map);
 	kfree(vol);
 	ntfs_debug("Failed, returning -EINVAL.");
 	lockdep_on();
-- 
2.43.0
[PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by DaeMyung Kang 3 days, 11 hours ago
get_nr_free_clusters() is run from the precalc_free_clusters() worker
queued at the end of ntfs_fill_super().  It is also the only place that
publishes the result by atomic64_set(&vol->free_clusters, ...), sets
NVolSetFreeClusterKnown(), and wakes vol->free_waitq.

The function currently allocates a temporary file_ra_state with
kzalloc() before that publication happens.  If the allocation fails,
get_nr_free_clusters() returns 0 without setting NVolFreeClusterKnown()
or waking vol->free_waitq, so callers that wait for the free count can
block forever.

The readahead state is only used synchronously while scanning the bitmap
and struct file_ra_state is small.  Keep it on the stack and pass it to
the readahead helper by address, eliminating the early allocation
failure path.  Zero-initialize the on-stack state because
file_ra_state_init() only sets ra_pages and prev_pos.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 7e3561265b47..14fd244f29fe 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1954,7 +1954,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
 	struct folio *folio;
 	pgoff_t index, max_index;
-	struct file_ra_state *ra;
+	struct file_ra_state ra = {};
 
 	ntfs_debug("Entering.");
 	/* Serialize accesses to the cluster bitmap. */
@@ -1962,11 +1962,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	if (NVolFreeClusterKnown(vol))
 		return atomic64_read(&vol->free_clusters);
 
-	ra = kzalloc(sizeof(*ra), GFP_NOFS);
-	if (!ra)
-		return 0;
-
-	file_ra_state_init(ra, mapping);
+	file_ra_state_init(&ra, mapping);
 
 	/*
 	 * Convert the number of bits into bytes rounded up, then convert into
@@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 		 * Get folio from page cache, getting it from backing store
 		 * if necessary, and increment the use count.
 		 */
-		folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
+		folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
 
 		/* Ignore pages which errored synchronously. */
 		if (IS_ERR(folio)) {
@@ -2024,7 +2020,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
 	else
 		atomic64_set(&vol->free_clusters, nr_free);
 
-	kfree(ra);
 	NVolSetFreeClusterKnown(vol);
 	wake_up_all(&vol->free_waitq);
 	ntfs_debug("Exiting.");
-- 
2.43.0
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by Hyunchul Lee 2 days, 21 hours ago
2026년 5월 21일 (목) 오후 7:18, DaeMyung Kang <charsyam@gmail.com>님이 작성:
>
> get_nr_free_clusters() is run from the precalc_free_clusters() worker
> queued at the end of ntfs_fill_super().  It is also the only place that
> publishes the result by atomic64_set(&vol->free_clusters, ...), sets
> NVolSetFreeClusterKnown(), and wakes vol->free_waitq.
>
> The function currently allocates a temporary file_ra_state with
> kzalloc() before that publication happens.  If the allocation fails,
> get_nr_free_clusters() returns 0 without setting NVolFreeClusterKnown()
> or waking vol->free_waitq, so callers that wait for the free count can
> block forever.
>
> The readahead state is only used synchronously while scanning the bitmap
> and struct file_ra_state is small.  Keep it on the stack and pass it to
> the readahead helper by address, eliminating the early allocation
> failure path.  Zero-initialize the on-stack state because
> file_ra_state_init() only sets ra_pages and prev_pos.
>
> Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
> ---
>  fs/ntfs/super.c | 11 +++--------
>  1 file changed, 3 insertions(+), 8 deletions(-)
>
> diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
> index 7e3561265b47..14fd244f29fe 100644
> --- a/fs/ntfs/super.c
> +++ b/fs/ntfs/super.c
> @@ -1954,7 +1954,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
>         struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
>         struct folio *folio;
>         pgoff_t index, max_index;
> -       struct file_ra_state *ra;
> +       struct file_ra_state ra = {};
>
>         ntfs_debug("Entering.");
>         /* Serialize accesses to the cluster bitmap. */
> @@ -1962,11 +1962,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
>         if (NVolFreeClusterKnown(vol))
>                 return atomic64_read(&vol->free_clusters);
>
> -       ra = kzalloc(sizeof(*ra), GFP_NOFS);
> -       if (!ra)
> -               return 0;
> -
> -       file_ra_state_init(ra, mapping);
> +       file_ra_state_init(&ra, mapping);
>
>         /*
>          * Convert the number of bits into bytes rounded up, then convert into
> @@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
>                  * Get folio from page cache, getting it from backing store
>                  * if necessary, and increment the use count.
>                  */
> -               folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
> +               folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);

This line modifies the __get_nr_free_mft_records function, not the
get_nr_free_clusters function.
Therefore the patch needs to be rebased on ntfs-next branch at
git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git.

>
>                 /* Ignore pages which errored synchronously. */
>                 if (IS_ERR(folio)) {
> @@ -2024,7 +2020,6 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
>         else
>                 atomic64_set(&vol->free_clusters, nr_free);
>
> -       kfree(ra);
>         NVolSetFreeClusterKnown(vol);
>         wake_up_all(&vol->free_waitq);
>         ntfs_debug("Exiting.");
> --
> 2.43.0
>


-- 
Thanks,
Hyunchul
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by Namjae Jeon 2 days, 21 hours ago
> >          * Convert the number of bits into bytes rounded up, then convert into
> > @@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
> >                  * Get folio from page cache, getting it from backing store
> >                  * if necessary, and increment the use count.
> >                  */
> > -               folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
> > +               folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
>
> This line modifies the __get_nr_free_mft_records function, not the
> get_nr_free_clusters function.
> Therefore the patch needs to be rebased on ntfs-next branch at
> git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git.
We can change__get_nr_free_mft_records() in the same way as
get_nr_free_clusters().
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by CharSyam 2 days, 20 hours ago
Thanks for the clarification.
I will rebase it onto ntfs-next and resend.

2026년 5월 22일 (금) 오전 9:28, Namjae Jeon <linkinjeon@kernel.org>님이 작성:
>
> > >          * Convert the number of bits into bytes rounded up, then convert into
> > > @@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
> > >                  * Get folio from page cache, getting it from backing store
> > >                  * if necessary, and increment the use count.
> > >                  */
> > > -               folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
> > > +               folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
> >
> > This line modifies the __get_nr_free_mft_records function, not the
> > get_nr_free_clusters function.
> > Therefore the patch needs to be rebased on ntfs-next branch at
> > git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git.
> We can change__get_nr_free_mft_records() in the same way as
> get_nr_free_clusters().
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by Namjae Jeon 2 days, 17 hours ago
On Fri, May 22, 2026 at 10:22 AM CharSyam <charsyam@gmail.com> wrote:
>
> Thanks for the clarification.
> I will rebase it onto ntfs-next and resend.
You can just resend this patch, not the entire series.
Thanks.
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by Namjae Jeon 3 days, 9 hours ago
>         /*
>          * Convert the number of bits into bytes rounded up, then convert into
> @@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
>                  * Get folio from page cache, getting it from backing store
>                  * if necessary, and increment the use count.
>                  */
> -               folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
> +               folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
This patch causes a build error when applied. Does it build
successfully for you?
Re: [PATCH v2 2/3] ntfs: avoid heap allocation for free-cluster readahead state
Posted by CharSyam 3 days, 8 hours ago
Hi Namjae,

Thanks for testing it.

It built successfully on my local setup with gcc 13.3.0
(gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0).  I tested it on top of
mainline commit 8bc67e4db64a with CONFIG_NTFS_FS=m using:

make O=/tmp/linux-ntfs-mainline-v2-build fs/ntfs/super.o
make O=/tmp/linux-ntfs-mainline-v2-build fs/ntfs/ntfs.ko

However, I found a portability issue in patch 2.  I used:

struct file_ra_state ra = {};

which gcc accepted here, but this can be rejected by stricter builds as
an empty initializer.  I changed it to:

struct file_ra_state ra = { 0 };

and re-tested patch 2 alone and patches 1-2 on a clean mainline tree;
both build fs/ntfs/super.o successfully.  The full series also builds
fs/ntfs/ntfs.ko.

I will send v3 with this fix.  If your build error is different from the
empty-initializer issue, could you please share the exact error log?

Thanks,
DaeMyung

2026년 5월 21일 (목) 오후 9:19, Namjae Jeon <linkinjeon@kernel.org>님이 작성:
>
> >         /*
> >          * Convert the number of bits into bytes rounded up, then convert into
> > @@ -1985,7 +1981,7 @@ s64 get_nr_free_clusters(struct ntfs_volume *vol)
> >                  * Get folio from page cache, getting it from backing store
> >                  * if necessary, and increment the use count.
> >                  */
> > -               folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
> > +               folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
> This patch causes a build error when applied. Does it build
> successfully for you?
[PATCH v2 3/3] ntfs: only alias volume $UpCase to default on exact match
Posted by DaeMyung Kang 3 days, 11 hours ago
load_and_init_upcase() currently aliases vol->upcase to the global
default upcase whenever the shared prefix matches, and then truncates
vol->upcase_len to that shorter prefix.  The result is correct only by
accident: upcase[] accesses in name collation are gated by upcase_len,
so the prefix-equality alias produces the same fold output as keeping
the volume's own shorter table.

Still, prefix equality is not equality: the volume table is logically
distinct from the default and should not be replaced by it unless they
are byte-for-byte identical.  Use memcmp() to compare the complete table
in one expression and drop the now-redundant upcase_len rewrite.

No user-visible change is expected for compliant volumes whose $UpCase
has exactly default_upcase_len entries; shorter volume tables are no
longer aliased to the default.

Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
---
 fs/ntfs/super.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 14fd244f29fe..971c2822f5e3 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1323,7 +1323,6 @@ static bool load_and_init_upcase(struct ntfs_volume *vol)
 	u8 *addr;
 	pgoff_t index, max_index;
 	unsigned int size;
-	int i, max;
 
 	ntfs_debug("Entering.");
 	/* Read upcase table and setup vol->upcase and vol->upcase_len. */
@@ -1374,16 +1373,11 @@ static bool load_and_init_upcase(struct ntfs_volume *vol)
 		mutex_unlock(&ntfs_lock);
 		return true;
 	}
-	max = default_upcase_len;
-	if (max > vol->upcase_len)
-		max = vol->upcase_len;
-	for (i = 0; i < max; i++)
-		if (vol->upcase[i] != default_upcase[i])
-			break;
-	if (i == max) {
+	if (default_upcase_len == vol->upcase_len &&
+	    !memcmp(vol->upcase, default_upcase,
+		    default_upcase_len * sizeof(*default_upcase))) {
 		kvfree(vol->upcase);
 		vol->upcase = default_upcase;
-		vol->upcase_len = max;
 		ntfs_nr_upcase_users++;
 		mutex_unlock(&ntfs_lock);
 		ntfs_debug("Volume specified $UpCase matches default. Using default.");
-- 
2.43.0
Re: [PATCH 0/3] ntfs: fix mount failure cleanup and free-count setup
Posted by Hyunchul Lee 3 days, 20 hours ago
Hi DaeMyung,

2026년 5월 21일 (목) 오전 2:01, DaeMyung Kang <charsyam@gmail.com>님이 작성:
>
> Hi Namjae,
>
> This small series is based on current ntfs-next commit d8756162c6ca
> ("ntfs: remove unsupported quota handling").

These patches appear to be intended for an out of tree version on Github.
They need to be ported for the Linux kernel.


>
> The previous quota_ino reference leak fix is intentionally dropped
> because that commit removes load_and_init_quota(), the $Quota/$Q state,
> and the quota out-of-date marker path.
>
> The remaining fixes cover mount failure cleanup, a free-cluster precalc
> OOM path that can otherwise leave waiters blocked forever, and the
> $UpCase default-table alias check.  The free-cluster patch also clears
> the per-bitmap-page free counters on the conservative zero-free-count
> path so later allocation heuristics stay conservative after the known
> flag is published.
>
> Testing:
> - For patch 1, I built a DEBUG_KMEMLEAK/DEBUG_FS kernel and injected an
>   ntfs_fill_super() failure immediately after load_system_files().  Before
>   the fix, kmemleak reported the leaked volume label allocated from
>   ntfs_ucstonls(); after the fix, the same failure path produced no
>   kmemleak report.
> - For patch 2, I used QEMU with a debug injection that forces the initial
>   precalc readahead-state allocation to fail.  Before the fix, statfs
>   remained blocked in D state in ntfs_statfs(); after the fix, the waiter
>   woke up and statfs returned with the conservative zero free-cluster
>   count.
>
> DaeMyung Kang (3):
>   ntfs: free volume-wide resources on fill_super failure
>   ntfs: wake free-cluster waiters when precalc allocation fails
>   ntfs: only alias volume $UpCase to default on exact match
>
>  super.c | 34 ++++++++++++++++++++++------------
>  1 file changed, 22 insertions(+), 12 deletions(-)
>
>
> base-commit: d8756162c6cab1ad4478539d33da6cab413af1ee
> --
> 2.43.0



--
Thanks,
Hyunchul