Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
31int 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 */
85int 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}