| 1 | |
| 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 | blk_opf_t op;
|
| 37 | sector_t sector = ntfs_bytes_to_bio_sector(start);
|
| 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_MASK) == 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 | u32 len;
|
| 101 |
|
| 102 | folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL);
|
| 103 | if (IS_ERR(folio)) {
|
| 104 | ntfs_error(sb, "Unable to read %ld page", idx);
|
| 105 | return PTR_ERR(folio);
|
| 106 | }
|
| 107 |
|
| 108 | offset = (loff_t)idx << PAGE_SHIFT;
|
| 109 | to = min_t(u32, end - offset, PAGE_SIZE);
|
| 110 | len = to - from;
|
| 111 |
|
| 112 | memcpy_to_folio(folio, from, buf + buf_off, len);
|
| 113 | buf_off += len;
|
| 114 | folio_mark_uptodate(folio);
|
| 115 | folio_mark_dirty(folio);
|
| 116 | folio_put(folio);
|
| 117 | }
|
| 118 |
|
| 119 | return 0;
|
| 120 | }
|