GNU Linux-libre 5.10.153-gnu1
[releases.git] / fs / iomap / seek.c
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 page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff,
19                 int whence)
20 {
21         const struct address_space_operations *ops = inode->i_mapping->a_ops;
22         unsigned int bsize = i_blocksize(inode), off;
23         bool seek_data = whence == SEEK_DATA;
24         loff_t poff = page_offset(page);
25
26         if (WARN_ON_ONCE(*lastoff >= poff + PAGE_SIZE))
27                 return false;
28
29         if (*lastoff < poff) {
30                 /*
31                  * Last offset smaller than the start of the page means we found
32                  * a hole:
33                  */
34                 if (whence == SEEK_HOLE)
35                         return true;
36                 *lastoff = poff;
37         }
38
39         /*
40          * Just check the page unless we can and should check block ranges:
41          */
42         if (bsize == PAGE_SIZE || !ops->is_partially_uptodate)
43                 return PageUptodate(page) == seek_data;
44
45         lock_page(page);
46         if (unlikely(page->mapping != inode->i_mapping))
47                 goto out_unlock_not_found;
48
49         for (off = 0; off < PAGE_SIZE; off += bsize) {
50                 if (offset_in_page(*lastoff) >= off + bsize)
51                         continue;
52                 if (ops->is_partially_uptodate(page, off, bsize) == seek_data) {
53                         unlock_page(page);
54                         return true;
55                 }
56                 *lastoff = poff + off + bsize;
57         }
58
59 out_unlock_not_found:
60         unlock_page(page);
61         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 page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length,
75                 int whence)
76 {
77         pgoff_t index = offset >> PAGE_SHIFT;
78         pgoff_t end = DIV_ROUND_UP(offset + length, PAGE_SIZE);
79         loff_t lastoff = offset;
80         struct pagevec pvec;
81
82         if (length <= 0)
83                 return -ENOENT;
84
85         pagevec_init(&pvec);
86
87         do {
88                 unsigned nr_pages, i;
89
90                 nr_pages = pagevec_lookup_range(&pvec, inode->i_mapping, &index,
91                                                 end - 1);
92                 if (nr_pages == 0)
93                         break;
94
95                 for (i = 0; i < nr_pages; i++) {
96                         struct page *page = pvec.pages[i];
97
98                         if (page_seek_hole_data(inode, page, &lastoff, whence))
99                                 goto check_range;
100                         lastoff = page_offset(page) + PAGE_SIZE;
101                 }
102                 pagevec_release(&pvec);
103         } while (index < end);
104
105         /* When no page at lastoff and we are not done, we found a hole. */
106         if (whence != SEEK_HOLE)
107                 goto not_found;
108
109 check_range:
110         if (lastoff < offset + length)
111                 goto out;
112 not_found:
113         lastoff = -ENOENT;
114 out:
115         pagevec_release(&pvec);
116         return lastoff;
117 }
118
119
120 static loff_t
121 iomap_seek_hole_actor(struct inode *inode, loff_t offset, loff_t length,
122                       void *data, struct iomap *iomap, struct iomap *srcmap)
123 {
124         switch (iomap->type) {
125         case IOMAP_UNWRITTEN:
126                 offset = page_cache_seek_hole_data(inode, offset, length,
127                                                    SEEK_HOLE);
128                 if (offset < 0)
129                         return length;
130                 fallthrough;
131         case IOMAP_HOLE:
132                 *(loff_t *)data = offset;
133                 return 0;
134         default:
135                 return length;
136         }
137 }
138
139 loff_t
140 iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
141 {
142         loff_t size = i_size_read(inode);
143         loff_t ret;
144
145         /* Nothing to be found before or beyond the end of the file. */
146         if (offset < 0 || offset >= size)
147                 return -ENXIO;
148
149         while (offset < size) {
150                 ret = iomap_apply(inode, offset, size - offset, IOMAP_REPORT,
151                                   ops, &offset, iomap_seek_hole_actor);
152                 if (ret < 0)
153                         return ret;
154                 if (ret == 0)
155                         break;
156                 offset += ret;
157         }
158
159         return offset;
160 }
161 EXPORT_SYMBOL_GPL(iomap_seek_hole);
162
163 static loff_t
164 iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
165                       void *data, struct iomap *iomap, struct iomap *srcmap)
166 {
167         switch (iomap->type) {
168         case IOMAP_HOLE:
169                 return length;
170         case IOMAP_UNWRITTEN:
171                 offset = page_cache_seek_hole_data(inode, offset, length,
172                                                    SEEK_DATA);
173                 if (offset < 0)
174                         return length;
175                 fallthrough;
176         default:
177                 *(loff_t *)data = offset;
178                 return 0;
179         }
180 }
181
182 loff_t
183 iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
184 {
185         loff_t size = i_size_read(inode);
186         loff_t ret;
187
188         /* Nothing to be found before or beyond the end of the file. */
189         if (offset < 0 || offset >= size)
190                 return -ENXIO;
191
192         while (offset < size) {
193                 ret = iomap_apply(inode, offset, size - offset, IOMAP_REPORT,
194                                   ops, &offset, iomap_seek_data_actor);
195                 if (ret < 0)
196                         return ret;
197                 if (ret == 0)
198                         return offset;
199                 offset += ret;
200         }
201
202         /* We've reached the end of the file without finding data */
203         return -ENXIO;
204 }
205 EXPORT_SYMBOL_GPL(iomap_seek_data);