[PATCH] ntfs: compute bi_sector in 512-byte units

From: Dennis Tighe

Date: Sun Aug 23 2026 - 03:13:38 EST


bi_sector counts in 512 byte sectors and not in multiples of the
volume's sector size. Under "normal" circumstances (with 512 byte
sectors in NTFS) the current code works as is; however, when we have
a 4k sector size on the volume the current usage of NTFS_B_TO_SECTOR()
and ntfs_bytes_to_sector() end up converting to the number of 4k
sectors after mount.

Reads work today on 4k volumes as bdev-io.c as performing the shift
correctly inline. With writes, we end up with significant silent disk
corruption on these volumes.

This fixes changes to use the new ntfs_bytes_to_bio_sector() function
everywhere we're performing this calculation (including the existing
read path). For the change in inode.c it removes a dead code block
rather than updating.

Fixes: 40796051991d ("ntfs: update in-memory, on-disk structures and headers")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Dennis Tighe <dennis.tighe@xxxxxxxxx>
---
I tested this with local volumes that were created with 4k logical
sector sizes. This change seems to prevent the primary silent corruption
that was occuring without it (due to writes being made in unrelated
places).

Note this was found and test with AI assistance.

fs/ntfs/bdev-io.c | 2 +-
fs/ntfs/compress.c | 2 +-
fs/ntfs/inode.c | 10 ++--------
fs/ntfs/mft.c | 12 ++++++------
fs/ntfs/ntfs.h | 10 ++++------
5 files changed, 14 insertions(+), 22 deletions(-)

diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c
index 86db4d9298ed..4f27eed3b072 100644
--- a/fs/ntfs/bdev-io.c
+++ b/fs/ntfs/bdev-io.c
@@ -34,7 +34,7 @@ int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t s
int error;
struct bio *bio;
blk_opf_t op;
- sector_t sector = start >> SECTOR_SHIFT;
+ sector_t sector = ntfs_bytes_to_bio_sector(start);

if (start & (SECTOR_SIZE - 1))
return -EINVAL;
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
index 2225630b19d7..197d8607fc63 100644
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -1414,7 +1414,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
bio_pos = ntfs_cluster_to_bytes(vol, bio_lcn);
bio = bio_alloc(vol->sb->s_bdev, DIV_ROUND_UP(bio_size, PAGE_SIZE),
REQ_OP_WRITE, GFP_NOIO);
- bio->bi_iter.bi_sector = ntfs_bytes_to_sector(vol, bio_pos);
+ bio->bi_iter.bi_sector = ntfs_bytes_to_bio_sector(bio_pos);

for (i = 0; bio_size; i++) {
unsigned int len = min_t(unsigned int, bio_size, PAGE_SIZE);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 32edb4045178..5aedc045f65a 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1852,7 +1852,7 @@ int ntfs_read_inode_mount(struct inode *vi)
struct mft_record *m = NULL;
struct attr_record *a;
struct ntfs_attr_search_ctx *ctx;
- unsigned int i, nr_blocks;
+ unsigned int i;
int err;
size_t new_rl_count;

@@ -1896,11 +1896,6 @@ int ntfs_read_inode_mount(struct inode *vi)
goto err_out;
}

- /* Determine the first block of the $MFT/$DATA attribute. */
- nr_blocks = ntfs_bytes_to_sector(vol, vol->mft_record_size);
- if (!nr_blocks)
- nr_blocks = 1;
-
/* Load $MFT/$DATA's first mft record. */
err = ntfs_bdev_read(sb->s_bdev, (char *)m,
ntfs_cluster_to_bytes(vol, vol->mft_lcn), i);
@@ -3780,8 +3775,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
GFP_NOIO);
bio->bi_iter.bi_sector =
- ntfs_bytes_to_sector(vol,
- ntfs_cluster_to_bytes(vol, lcn) +
+ ntfs_bytes_to_bio_sector(ntfs_cluster_to_bytes(vol, lcn) +
lcn_folio_off);

length = min_t(unsigned long,
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
index 984a0827f9ac..299a1d0dd6df 100644
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -499,8 +499,8 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,

bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
bio->bi_iter.bi_sector =
- NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
- lcn_folio_off + folio_ofs);
+ ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
+ lcn_folio_off + folio_ofs);

if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs))
err = submit_bio_wait(bio);
@@ -592,8 +592,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn

bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
bio->bi_iter.bi_sector =
- NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
- clu_off);
+ ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
+ clu_off);

if (!bio_add_folio(bio, folio, folio_size,
ni->folio_ofs + offset)) {
@@ -2740,8 +2740,8 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
GFP_NOIO);
bio->bi_iter.bi_sector =
- ntfs_bytes_to_sector(vol,
- ntfs_cluster_to_bytes(vol, lcn) + off);
+ ntfs_bytes_to_bio_sector(
+ ntfs_cluster_to_bytes(vol, lcn) + off);
}

if (vol->cluster_size == NTFS_BLOCK_SIZE &&
diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
index df5a75d506f6..45f77848a9cf 100644
--- a/fs/ntfs/ntfs.h
+++ b/fs/ntfs/ntfs.h
@@ -19,6 +19,7 @@
#include <linux/nls.h>
#include <linux/smp.h>
#include <linux/pagemap.h>
+#include <linux/blk_types.h>
#include <linux/uidgid.h>

#include "volume.h"
@@ -71,8 +72,6 @@
#define NTFS_CLU_TO_POFS(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) & \
~PAGE_MASK)

-#define NTFS_B_TO_SECTOR(vol, b) ((b) >> ((vol)->sb)->s_blocksize_bits)
-
enum {
NTFS_BLOCK_SIZE = 512,
NTFS_BLOCK_SIZE_BITS = 9,
@@ -154,11 +153,10 @@ static inline u64 ntfs_cluster_to_poff(const struct ntfs_volume *vol,
return (clu << vol->cluster_size_bits) & ~PAGE_MASK;
}

-/* Convert byte offset to sector (block) number. */
-static inline sector_t ntfs_bytes_to_sector(const struct ntfs_volume *vol,
- u64 bytes)
+/* Convert a byte offset on the volume to a bio sector number. */
+static inline sector_t ntfs_bytes_to_bio_sector(u64 bytes)
{
- return bytes >> vol->sb->s_blocksize_bits;
+ return bytes >> SECTOR_SHIFT;
}

/* Global variables. */
--
2.43.0