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