xref: /linux/fs/ntfs/bdev-io.c (revision cdd4dc3aebeab43a72ce0bc2b5bab6f0a80b97a5)
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