Files
linux/fs/ntfs/bdev-io.c
T
Dennis Tighe 6faa235a64 ntfs: compute bi_sector in 512-byte units
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: 4079605199 ("ntfs: update in-memory, on-disk structures and headers")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Dennis Tighe <dennis.tighe@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-27 21:47:38 +09:00

121 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* NTFS block device I/O.
*
* Copyright (c) 2026 LG Electronics Co., Ltd.
*/
#include <linux/blkdev.h>
#include "ntfs.h"
/*
* ntfs_bdev_read - Read data directly from block device using bio
* @bdev: block device to read from
* @data: destination buffer
* @start: starting byte offset on the block device
* @size: number of bytes to read
*
* Reads @size bytes starting from byte offset @start directly from the block
* device using one or more BIOs. This function bypasses the page cache
* completely and performs synchronous I/O with REQ_META | REQ_SYNC flags set.
*
* The @start offset must be sector-aligned (512 bytes). If it is not aligned,
* the function will return -EINVAL.
*
* If the destination buffer @data is not a vmalloc address, it falls back
* to the more efficient bdev_rw_virt() helper.
*
* Return: 0 on success, negative error code on failure.
*/
int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size)
{
unsigned int done = 0, added;
int error;
struct bio *bio;
blk_opf_t op;
sector_t sector = ntfs_bytes_to_bio_sector(start);
if (start & (SECTOR_SIZE - 1))
return -EINVAL;
op = REQ_OP_READ | REQ_META | REQ_SYNC;
if (!is_vmalloc_addr(data))
return bdev_rw_virt(bdev, sector, data, size, op);
bio = bio_alloc(bdev,
bio_max_segs(DIV_ROUND_UP(size, PAGE_SIZE)),
op, GFP_KERNEL);
bio->bi_iter.bi_sector = sector;
do {
added = bio_add_vmalloc_chunk(bio, data + done, size - done);
if (!added) {
struct bio *prev = bio;
bio = bio_alloc(prev->bi_bdev,
bio_max_segs(DIV_ROUND_UP(size - done, PAGE_SIZE)),
prev->bi_opf, GFP_KERNEL);
bio->bi_iter.bi_sector = bio_end_sector(prev);
bio_chain(prev, bio);
submit_bio(prev);
}
done += added;
} while (done < size);
error = submit_bio_wait(bio);
bio_put(bio);
if ((op & REQ_OP_MASK) == REQ_OP_READ)
invalidate_kernel_vmap_range(data, size);
return error;
}
/*
* ntfs_bdev_write - Update block device contents via page cache
* @sb: super block of the mounted NTFS filesystem
* @buf: source buffer containing data to write
* @start: starting byte offset on the block device
* @size: number of bytes to write
*
* Writes @size bytes from @buf to the block device (sb->s_bdev) starting
* at byte offset @start. The write is performed entirely through the page
* cache of the block device's address space.
*/
int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size)
{
pgoff_t idx, idx_end;
loff_t offset, end = start + size;
u32 from, to, buf_off = 0;
struct folio *folio;
idx = start >> PAGE_SHIFT;
idx_end = end >> PAGE_SHIFT;
from = start & ~PAGE_MASK;
if (idx == idx_end)
idx_end++;
for (; idx < idx_end; idx++, from = 0) {
u32 len;
folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL);
if (IS_ERR(folio)) {
ntfs_error(sb, "Unable to read %ld page", idx);
return PTR_ERR(folio);
}
offset = (loff_t)idx << PAGE_SHIFT;
to = min_t(u32, end - offset, PAGE_SIZE);
len = to - from;
memcpy_to_folio(folio, from, buf + buf_off, len);
buf_off += len;
folio_mark_uptodate(folio);
folio_mark_dirty(folio);
folio_put(folio);
}
return 0;
}