Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright (C) 2017 Red Hat, Inc.
4 : : * Copyright (c) 2018 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 : : /*
14 : : * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff.
15 : : * Returns true if found and updates @lastoff to the offset in file.
16 : : */
17 : : static bool
18 : 0 : page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff,
19 : : int whence)
20 : : {
21 : 0 : const struct address_space_operations *ops = inode->i_mapping->a_ops;
22 [ # # ]: 0 : unsigned int bsize = i_blocksize(inode), off;
23 : 0 : bool seek_data = whence == SEEK_DATA;
24 [ # # ]: 0 : loff_t poff = page_offset(page);
25 : :
26 [ # # # # ]: 0 : if (WARN_ON_ONCE(*lastoff >= poff + PAGE_SIZE))
27 : : return false;
28 : :
29 [ # # ]: 0 : if (*lastoff < poff) {
30 : : /*
31 : : * Last offset smaller than the start of the page means we found
32 : : * a hole:
33 : : */
34 [ # # ]: 0 : if (whence == SEEK_HOLE)
35 : : return true;
36 : 0 : *lastoff = poff;
37 : : }
38 : :
39 : : /*
40 : : * Just check the page unless we can and should check block ranges:
41 : : */
42 [ # # # # ]: 0 : if (bsize == PAGE_SIZE || !ops->is_partially_uptodate)
43 : 0 : return PageUptodate(page) == seek_data;
44 : :
45 : 0 : lock_page(page);
46 [ # # ]: 0 : if (unlikely(page->mapping != inode->i_mapping))
47 : 0 : goto out_unlock_not_found;
48 : :
49 [ # # ]: 0 : for (off = 0; off < PAGE_SIZE; off += bsize) {
50 [ # # ]: 0 : if (offset_in_page(*lastoff) >= off + bsize)
51 : 0 : continue;
52 [ # # ]: 0 : if (ops->is_partially_uptodate(page, off, bsize) == seek_data) {
53 : 0 : unlock_page(page);
54 : 0 : return true;
55 : : }
56 : 0 : *lastoff = poff + off + bsize;
57 : : }
58 : :
59 : 0 : out_unlock_not_found:
60 : 0 : unlock_page(page);
61 : 0 : return false;
62 : : }
63 : :
64 : : /*
65 : : * Seek for SEEK_DATA / SEEK_HOLE in the page cache.
66 : : *
67 : : * Within unwritten extents, the page cache determines which parts are holes
68 : : * and which are data: uptodate buffer heads count as data; everything else
69 : : * counts as a hole.
70 : : *
71 : : * Returns the resulting offset on successs, and -ENOENT otherwise.
72 : : */
73 : : static loff_t
74 : 0 : page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length,
75 : : int whence)
76 : : {
77 : 0 : pgoff_t index = offset >> PAGE_SHIFT;
78 : 0 : pgoff_t end = DIV_ROUND_UP(offset + length, PAGE_SIZE);
79 : 0 : loff_t lastoff = offset;
80 : 0 : struct pagevec pvec;
81 : :
82 [ # # ]: 0 : if (length <= 0)
83 : : return -ENOENT;
84 : :
85 : 0 : pagevec_init(&pvec);
86 : :
87 : 0 : do {
88 : 0 : unsigned nr_pages, i;
89 : :
90 : 0 : nr_pages = pagevec_lookup_range(&pvec, inode->i_mapping, &index,
91 : : end - 1);
92 [ # # ]: 0 : if (nr_pages == 0)
93 : : break;
94 : :
95 [ # # ]: 0 : for (i = 0; i < nr_pages; i++) {
96 : 0 : struct page *page = pvec.pages[i];
97 : :
98 [ # # ]: 0 : if (page_seek_hole_data(inode, page, &lastoff, whence))
99 : 0 : goto check_range;
100 : 0 : lastoff = page_offset(page) + PAGE_SIZE;
101 : : }
102 [ # # ]: 0 : pagevec_release(&pvec);
103 [ # # ]: 0 : } while (index < end);
104 : :
105 : : /* When no page at lastoff and we are not done, we found a hole. */
106 [ # # ]: 0 : if (whence != SEEK_HOLE)
107 : 0 : goto not_found;
108 : :
109 : 0 : check_range:
110 [ # # ]: 0 : if (lastoff < offset + length)
111 : 0 : goto out;
112 : 0 : not_found:
113 : 0 : lastoff = -ENOENT;
114 : 0 : out:
115 [ # # ]: 0 : pagevec_release(&pvec);
116 : 0 : return lastoff;
117 : : }
118 : :
119 : :
120 : : static loff_t
121 : 0 : iomap_seek_hole_actor(struct inode *inode, loff_t offset, loff_t length,
122 : : void *data, struct iomap *iomap, struct iomap *srcmap)
123 : : {
124 [ # # # ]: 0 : switch (iomap->type) {
125 : 0 : case IOMAP_UNWRITTEN:
126 : 0 : offset = page_cache_seek_hole_data(inode, offset, length,
127 : : SEEK_HOLE);
128 [ # # ]: 0 : if (offset < 0)
129 : : return length;
130 : : /* fall through */
131 : : case IOMAP_HOLE:
132 : 0 : *(loff_t *)data = offset;
133 : 0 : return 0;
134 : : default:
135 : : return length;
136 : : }
137 : : }
138 : :
139 : : loff_t
140 : 0 : iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
141 : : {
142 [ # # ]: 0 : loff_t size = i_size_read(inode);
143 : 0 : loff_t length = size - offset;
144 : 0 : loff_t ret;
145 : :
146 : : /* Nothing to be found before or beyond the end of the file. */
147 [ # # ]: 0 : if (offset < 0 || offset >= size)
148 : : return -ENXIO;
149 : :
150 [ # # ]: 0 : while (length > 0) {
151 : 0 : ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
152 : : &offset, iomap_seek_hole_actor);
153 [ # # ]: 0 : if (ret < 0)
154 : 0 : return ret;
155 [ # # ]: 0 : if (ret == 0)
156 : : break;
157 : :
158 : 0 : offset += ret;
159 : 0 : length -= ret;
160 : : }
161 : :
162 : 0 : return offset;
163 : : }
164 : : EXPORT_SYMBOL_GPL(iomap_seek_hole);
165 : :
166 : : static loff_t
167 : 0 : iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
168 : : void *data, struct iomap *iomap, struct iomap *srcmap)
169 : : {
170 [ # # # ]: 0 : switch (iomap->type) {
171 : : case IOMAP_HOLE:
172 : : return length;
173 : 0 : case IOMAP_UNWRITTEN:
174 : 0 : offset = page_cache_seek_hole_data(inode, offset, length,
175 : : SEEK_DATA);
176 [ # # ]: 0 : if (offset < 0)
177 : : return length;
178 : : /*FALLTHRU*/
179 : : default:
180 : 0 : *(loff_t *)data = offset;
181 : 0 : return 0;
182 : : }
183 : : }
184 : :
185 : : loff_t
186 : 0 : iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
187 : : {
188 [ # # ]: 0 : loff_t size = i_size_read(inode);
189 : 0 : loff_t length = size - offset;
190 : 0 : loff_t ret;
191 : :
192 : : /* Nothing to be found before or beyond the end of the file. */
193 [ # # ]: 0 : if (offset < 0 || offset >= size)
194 : : return -ENXIO;
195 : :
196 [ # # ]: 0 : while (length > 0) {
197 : 0 : ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
198 : : &offset, iomap_seek_data_actor);
199 [ # # ]: 0 : if (ret < 0)
200 : 0 : return ret;
201 [ # # ]: 0 : if (ret == 0)
202 : : break;
203 : :
204 : 0 : offset += ret;
205 : 0 : length -= ret;
206 : : }
207 : :
208 [ # # ]: 0 : if (length <= 0)
209 : : return -ENXIO;
210 : 0 : return offset;
211 : : }
212 : : EXPORT_SYMBOL_GPL(iomap_seek_data);
|