1*5218cd10SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later 2*5218cd10SNamjae Jeon /* 3*5218cd10SNamjae Jeon * NTFS block device I/O. 4*5218cd10SNamjae Jeon * 5*5218cd10SNamjae Jeon * Copyright (c) 2026 LG Electronics Co., Ltd. 6*5218cd10SNamjae Jeon */ 7*5218cd10SNamjae Jeon 8*5218cd10SNamjae Jeon #include <linux/blkdev.h> 9*5218cd10SNamjae Jeon 10*5218cd10SNamjae Jeon #include "ntfs.h" 11*5218cd10SNamjae Jeon 12*5218cd10SNamjae Jeon /* 13*5218cd10SNamjae Jeon * ntfs_bdev_read - Read data directly from block device using bio 14*5218cd10SNamjae Jeon * @bdev: block device to read from 15*5218cd10SNamjae Jeon * @data: destination buffer 16*5218cd10SNamjae Jeon * @start: starting byte offset on the block device 17*5218cd10SNamjae Jeon * @size: number of bytes to read 18*5218cd10SNamjae Jeon * 19*5218cd10SNamjae Jeon * Reads @size bytes starting from byte offset @start directly from the block 20*5218cd10SNamjae Jeon * device using one or more BIOs. This function bypasses the page cache 21*5218cd10SNamjae Jeon * completely and performs synchronous I/O with REQ_META | REQ_SYNC flags set. 22*5218cd10SNamjae Jeon * 23*5218cd10SNamjae Jeon * The @start offset must be sector-aligned (512 bytes). If it is not aligned, 24*5218cd10SNamjae Jeon * the function will return -EINVAL. 25*5218cd10SNamjae Jeon * 26*5218cd10SNamjae Jeon * If the destination buffer @data is not a vmalloc address, it falls back 27*5218cd10SNamjae Jeon * to the more efficient bdev_rw_virt() helper. 28*5218cd10SNamjae Jeon * 29*5218cd10SNamjae Jeon * Return: 0 on success, negative error code on failure. 30*5218cd10SNamjae Jeon */ 31*5218cd10SNamjae Jeon int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size) 32*5218cd10SNamjae Jeon { 33*5218cd10SNamjae Jeon unsigned int done = 0, added; 34*5218cd10SNamjae Jeon int error; 35*5218cd10SNamjae Jeon struct bio *bio; 36*5218cd10SNamjae Jeon enum req_op op; 37*5218cd10SNamjae Jeon sector_t sector = start >> SECTOR_SHIFT; 38*5218cd10SNamjae Jeon 39*5218cd10SNamjae Jeon if (start & (SECTOR_SIZE - 1)) 40*5218cd10SNamjae Jeon return -EINVAL; 41*5218cd10SNamjae Jeon 42*5218cd10SNamjae Jeon op = REQ_OP_READ | REQ_META | REQ_SYNC; 43*5218cd10SNamjae Jeon if (!is_vmalloc_addr(data)) 44*5218cd10SNamjae Jeon return bdev_rw_virt(bdev, sector, data, size, op); 45*5218cd10SNamjae Jeon 46*5218cd10SNamjae Jeon bio = bio_alloc(bdev, 47*5218cd10SNamjae Jeon bio_max_segs(DIV_ROUND_UP(size, PAGE_SIZE)), 48*5218cd10SNamjae Jeon op, GFP_KERNEL); 49*5218cd10SNamjae Jeon bio->bi_iter.bi_sector = sector; 50*5218cd10SNamjae Jeon 51*5218cd10SNamjae Jeon do { 52*5218cd10SNamjae Jeon added = bio_add_vmalloc_chunk(bio, data + done, size - done); 53*5218cd10SNamjae Jeon if (!added) { 54*5218cd10SNamjae Jeon struct bio *prev = bio; 55*5218cd10SNamjae Jeon 56*5218cd10SNamjae Jeon bio = bio_alloc(prev->bi_bdev, 57*5218cd10SNamjae Jeon bio_max_segs(DIV_ROUND_UP(size - done, PAGE_SIZE)), 58*5218cd10SNamjae Jeon prev->bi_opf, GFP_KERNEL); 59*5218cd10SNamjae Jeon bio->bi_iter.bi_sector = bio_end_sector(prev); 60*5218cd10SNamjae Jeon bio_chain(prev, bio); 61*5218cd10SNamjae Jeon submit_bio(prev); 62*5218cd10SNamjae Jeon } 63*5218cd10SNamjae Jeon done += added; 64*5218cd10SNamjae Jeon } while (done < size); 65*5218cd10SNamjae Jeon 66*5218cd10SNamjae Jeon error = submit_bio_wait(bio); 67*5218cd10SNamjae Jeon bio_put(bio); 68*5218cd10SNamjae Jeon 69*5218cd10SNamjae Jeon if (op == REQ_OP_READ) 70*5218cd10SNamjae Jeon invalidate_kernel_vmap_range(data, size); 71*5218cd10SNamjae Jeon return error; 72*5218cd10SNamjae Jeon } 73*5218cd10SNamjae Jeon 74*5218cd10SNamjae Jeon /* 75*5218cd10SNamjae Jeon * ntfs_bdev_write - Update block device contents via page cache 76*5218cd10SNamjae Jeon * @sb: super block of the mounted NTFS filesystem 77*5218cd10SNamjae Jeon * @buf: source buffer containing data to write 78*5218cd10SNamjae Jeon * @start: starting byte offset on the block device 79*5218cd10SNamjae Jeon * @size: number of bytes to write 80*5218cd10SNamjae Jeon * 81*5218cd10SNamjae Jeon * Writes @size bytes from @buf to the block device (sb->s_bdev) starting 82*5218cd10SNamjae Jeon * at byte offset @start. The write is performed entirely through the page 83*5218cd10SNamjae Jeon * cache of the block device's address space. 84*5218cd10SNamjae Jeon */ 85*5218cd10SNamjae Jeon int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size) 86*5218cd10SNamjae Jeon { 87*5218cd10SNamjae Jeon pgoff_t idx, idx_end; 88*5218cd10SNamjae Jeon loff_t offset, end = start + size; 89*5218cd10SNamjae Jeon u32 from, to, buf_off = 0; 90*5218cd10SNamjae Jeon struct folio *folio; 91*5218cd10SNamjae Jeon 92*5218cd10SNamjae Jeon idx = start >> PAGE_SHIFT; 93*5218cd10SNamjae Jeon idx_end = end >> PAGE_SHIFT; 94*5218cd10SNamjae Jeon from = start & ~PAGE_MASK; 95*5218cd10SNamjae Jeon 96*5218cd10SNamjae Jeon if (idx == idx_end) 97*5218cd10SNamjae Jeon idx_end++; 98*5218cd10SNamjae Jeon 99*5218cd10SNamjae Jeon for (; idx < idx_end; idx++, from = 0) { 100*5218cd10SNamjae Jeon folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL); 101*5218cd10SNamjae Jeon if (IS_ERR(folio)) { 102*5218cd10SNamjae Jeon ntfs_error(sb, "Unable to read %ld page", idx); 103*5218cd10SNamjae Jeon return PTR_ERR(folio); 104*5218cd10SNamjae Jeon } 105*5218cd10SNamjae Jeon 106*5218cd10SNamjae Jeon offset = (loff_t)idx << PAGE_SHIFT; 107*5218cd10SNamjae Jeon to = min_t(u32, end - offset, PAGE_SIZE); 108*5218cd10SNamjae Jeon 109*5218cd10SNamjae Jeon memcpy_to_folio(folio, from, buf + buf_off, to); 110*5218cd10SNamjae Jeon buf_off += to; 111*5218cd10SNamjae Jeon folio_mark_uptodate(folio); 112*5218cd10SNamjae Jeon folio_mark_dirty(folio); 113*5218cd10SNamjae Jeon folio_put(folio); 114*5218cd10SNamjae Jeon } 115*5218cd10SNamjae Jeon 116*5218cd10SNamjae Jeon return 0; 117*5218cd10SNamjae Jeon } 118