// SPDX-License-Identifier: GPL-2.0-or-later /* * NTFS block device I/O. * * Copyright (c) 2026 LG Electronics Co., Ltd. */ #include #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; enum req_op op; sector_t sector = start >> SECTOR_SHIFT; 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_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) { 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); memcpy_to_folio(folio, from, buf + buf_off, to); buf_off += to; folio_mark_uptodate(folio); folio_mark_dirty(folio); folio_put(folio); } return 0; }