close_range: unshare all fds for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC
[linux-2.6-microblaze.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 length = size - offset;
144         loff_t ret;
145
146         /* Nothing to be found before or beyond the end of the file. */
147         if (offset < 0 || offset >= size)
148                 return -ENXIO;
149
150         while (length > 0) {
151                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
152                                   &offset, iomap_seek_hole_actor);
153                 if (ret < 0)
154                         return ret;
155                 if (ret == 0)
156                         break;
157
158                 offset += ret;
159                 length -= ret;
160         }
161
162         return offset;
163 }
164 EXPORT_SYMBOL_GPL(iomap_seek_hole);
165
166 static loff_t
167 iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
168                       void *data, struct iomap *iomap, struct iomap *srcmap)
169 {
170         switch (iomap->type) {
171         case IOMAP_HOLE:
172                 return length;
173         case IOMAP_UNWRITTEN:
174                 offset = page_cache_seek_hole_data(inode, offset, length,
175                                                    SEEK_DATA);
176                 if (offset < 0)
177                         return length;
178                 fallthrough;
179         default:
180                 *(loff_t *)data = offset;
181                 return 0;
182         }
183 }
184
185 loff_t
186 iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
187 {
188         loff_t size = i_size_read(inode);
189         loff_t length = size - offset;
190         loff_t ret;
191
192         /* Nothing to be found before or beyond the end of the file. */
193         if (offset < 0 || offset >= size)
194                 return -ENXIO;
195
196         while (length > 0) {
197                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
198                                   &offset, iomap_seek_data_actor);
199                 if (ret < 0)
200                         return ret;
201                 if (ret == 0)
202                         break;
203
204                 offset += ret;
205                 length -= ret;
206         }
207
208         if (length <= 0)
209                 return -ENXIO;
210         return offset;
211 }
212 EXPORT_SYMBOL_GPL(iomap_seek_data);