1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2017 Red Hat, Inc. |
4 | * Copyright (c) 2018-2021 Christoph Hellwig. |
5 | */ |
6 | #include <linux/module.h> |
7 | #include <linux/compiler.h> |
8 | #include <linux/fs.h> |
9 | #include <linux/iomap.h> |
10 | #include <linux/pagemap.h> |
11 | #include <linux/pagevec.h> |
12 | |
13 | static loff_t iomap_seek_hole_iter(const struct iomap_iter *iter, |
14 | loff_t *hole_pos) |
15 | { |
16 | loff_t length = iomap_length(iter); |
17 | |
18 | switch (iter->iomap.type) { |
19 | case IOMAP_UNWRITTEN: |
20 | *hole_pos = mapping_seek_hole_data(iter->inode->i_mapping, |
21 | start: iter->pos, end: iter->pos + length, SEEK_HOLE); |
22 | if (*hole_pos == iter->pos + length) |
23 | return length; |
24 | return 0; |
25 | case IOMAP_HOLE: |
26 | *hole_pos = iter->pos; |
27 | return 0; |
28 | default: |
29 | return length; |
30 | } |
31 | } |
32 | |
33 | loff_t |
34 | iomap_seek_hole(struct inode *inode, loff_t pos, const struct iomap_ops *ops) |
35 | { |
36 | loff_t size = i_size_read(inode); |
37 | struct iomap_iter iter = { |
38 | .inode = inode, |
39 | .pos = pos, |
40 | .flags = IOMAP_REPORT, |
41 | }; |
42 | int ret; |
43 | |
44 | /* Nothing to be found before or beyond the end of the file. */ |
45 | if (pos < 0 || pos >= size) |
46 | return -ENXIO; |
47 | |
48 | iter.len = size - pos; |
49 | while ((ret = iomap_iter(iter: &iter, ops)) > 0) |
50 | iter.processed = iomap_seek_hole_iter(iter: &iter, hole_pos: &pos); |
51 | if (ret < 0) |
52 | return ret; |
53 | if (iter.len) /* found hole before EOF */ |
54 | return pos; |
55 | return size; |
56 | } |
57 | EXPORT_SYMBOL_GPL(iomap_seek_hole); |
58 | |
59 | static loff_t iomap_seek_data_iter(const struct iomap_iter *iter, |
60 | loff_t *hole_pos) |
61 | { |
62 | loff_t length = iomap_length(iter); |
63 | |
64 | switch (iter->iomap.type) { |
65 | case IOMAP_HOLE: |
66 | return length; |
67 | case IOMAP_UNWRITTEN: |
68 | *hole_pos = mapping_seek_hole_data(iter->inode->i_mapping, |
69 | start: iter->pos, end: iter->pos + length, SEEK_DATA); |
70 | if (*hole_pos < 0) |
71 | return length; |
72 | return 0; |
73 | default: |
74 | *hole_pos = iter->pos; |
75 | return 0; |
76 | } |
77 | } |
78 | |
79 | loff_t |
80 | iomap_seek_data(struct inode *inode, loff_t pos, const struct iomap_ops *ops) |
81 | { |
82 | loff_t size = i_size_read(inode); |
83 | struct iomap_iter iter = { |
84 | .inode = inode, |
85 | .pos = pos, |
86 | .flags = IOMAP_REPORT, |
87 | }; |
88 | int ret; |
89 | |
90 | /* Nothing to be found before or beyond the end of the file. */ |
91 | if (pos < 0 || pos >= size) |
92 | return -ENXIO; |
93 | |
94 | iter.len = size - pos; |
95 | while ((ret = iomap_iter(iter: &iter, ops)) > 0) |
96 | iter.processed = iomap_seek_data_iter(iter: &iter, hole_pos: &pos); |
97 | if (ret < 0) |
98 | return ret; |
99 | if (iter.len) /* found data before EOF */ |
100 | return pos; |
101 | /* We've reached the end of the file without finding data */ |
102 | return -ENXIO; |
103 | } |
104 | EXPORT_SYMBOL_GPL(iomap_seek_data); |
105 | |