super.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-)
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
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
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!
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
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
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
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
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
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
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
> > * 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().
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().
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.
> /* > * 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?
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?
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
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
© 2016 - 2026 Red Hat, Inc.