[PATCH] fs/ntfs3: widen wnd_bitmap::free_bits to u32
From: Zhan Xusheng
Date: Thu Oct 08 2026 - 21:36:57 EST
free_bits[] holds, per bitmap window, the number of free bits in that
window. A window is one block, so the value ranges over
[0, 8 * sb->s_blocksize], but the array element type is u16.
ntfs_init_from_boot() picks the block size as
sb_set_blocksize(sb, min_t(u32, sbi->cluster_size, PAGE_SIZE));
and ntfs3 accepts cluster sizes up to 2 MB, as true_sectors_per_clst()
and the comment above it describe. Nothing rejects a block size above
4096. So on a kernel with PAGE_SIZE >= 8192 (arm64 with 16K or 64K
pages, powerpc with 64K pages) mounting a volume whose cluster size is
at least 8192, the block size is at least 8192 and a window holds at
least 65536 bits, one more than u16 can represent.
A completely free window therefore stores 0, and five sites read 0 as
"this window has no free bits":
bitmap.c:533 wnd_rescan() treats it as all ones and skips it
bitmap.c:950 wnd_is_used() skips the on-disk check, returns true
bitmap.c:1155 wnd_find() skips the window when looking for space
bitmap.c:1243 wnd_find() the "window is empty" path never fires
bitmap.c:1450 ntfs_trim_fs() skips the window when discarding
The free space in such a window is then never allocated and never
trimmed, while total_zeroes, which is size_t, still counts it, so statfs
advertises space that cannot be used. wnd_is_used() reporting a free
range as used also costs mark_as_free_ex() its per-cluster check and
ntfs_mark_rec_free() its already-free detection.
bits_last, declared two lines below free_bits, is u32 and holds a value
from the same domain. Make free_bits u32 as well. The array grows from
2 to 4 bytes per block of bitmap; for a 1 TB volume with 4K clusters
that is 32 KB instead of 16 KB.
Signed-off-by: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
Fixes: 4534a70b7056 ("fs/ntfs3: Add headers and misc files")
---
fs/ntfs3/bitmap.c | 10 +++++-----
fs/ntfs3/ntfs_fs.h | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/fs/ntfs3/bitmap.c b/fs/ntfs3/bitmap.c
index 60b362f388d96..41ea3fad99af0 100644
--- a/fs/ntfs3/bitmap.c
+++ b/fs/ntfs3/bitmap.c
@@ -677,7 +677,7 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits)
wnd->bits_last = wbits;
wnd->free_bits =
- kvmalloc_array(wnd->nwnd, sizeof(u16), GFP_KERNEL | __GFP_ZERO);
+ kvmalloc_array(wnd->nwnd, sizeof(u32), GFP_KERNEL | __GFP_ZERO);
if (!wnd->free_bits)
return -ENOMEM;
@@ -1330,7 +1330,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits)
u32 b0, new_last;
size_t bits, iw, new_wnd;
size_t old_bits = wnd->nbits;
- u16 *new_free;
+ u32 *new_free;
if (new_bits <= old_bits)
return -EINVAL;
@@ -1342,13 +1342,13 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits)
new_last = wbits;
if (new_wnd != wnd->nwnd) {
- new_free = kmalloc_array(new_wnd, sizeof(u16), GFP_NOFS);
+ new_free = kmalloc_array(new_wnd, sizeof(u32), GFP_NOFS);
if (!new_free)
return -ENOMEM;
- memcpy(new_free, wnd->free_bits, wnd->nwnd * sizeof(short));
+ memcpy(new_free, wnd->free_bits, wnd->nwnd * sizeof(u32));
memset(new_free + wnd->nwnd, 0,
- (new_wnd - wnd->nwnd) * sizeof(short));
+ (new_wnd - wnd->nwnd) * sizeof(u32));
kvfree(wnd->free_bits);
wnd->free_bits = new_free;
}
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 5811d89d67b39..6f05b1bbb565a 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -152,7 +152,7 @@ struct wnd_bitmap {
size_t nbits;
size_t total_zeroes; // Total number of free bits.
- u16 *free_bits; // Free bits in each window.
+ u32 *free_bits; // Free bits in each window.
size_t nwnd;
u32 bits_last; // Bits in last window.
base-commit: 22430ae5d90ab288b0ee2ad99ae941f4a666b694
--
2.43.0