Merge tag 'iomap-5.13-merge-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[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 static loff_t
14 iomap_seek_hole_actor(struct inode *inode, loff_t start, loff_t length,
15                       void *data, struct iomap *iomap, struct iomap *srcmap)
16 {
17         loff_t offset = start;
18
19         switch (iomap->type) {
20         case IOMAP_UNWRITTEN:
21                 offset = mapping_seek_hole_data(inode->i_mapping, start,
22                                 start + length, SEEK_HOLE);
23                 if (offset == start + length)
24                         return length;
25                 fallthrough;
26         case IOMAP_HOLE:
27                 *(loff_t *)data = offset;
28                 return 0;
29         default:
30                 return length;
31         }
32 }
33
34 loff_t
35 iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
36 {
37         loff_t size = i_size_read(inode);
38         loff_t length = size - offset;
39         loff_t ret;
40
41         /* Nothing to be found before or beyond the end of the file. */
42         if (offset < 0 || offset >= size)
43                 return -ENXIO;
44
45         while (length > 0) {
46                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
47                                   &offset, iomap_seek_hole_actor);
48                 if (ret < 0)
49                         return ret;
50                 if (ret == 0)
51                         break;
52
53                 offset += ret;
54                 length -= ret;
55         }
56
57         return offset;
58 }
59 EXPORT_SYMBOL_GPL(iomap_seek_hole);
60
61 static loff_t
62 iomap_seek_data_actor(struct inode *inode, loff_t start, loff_t length,
63                       void *data, struct iomap *iomap, struct iomap *srcmap)
64 {
65         loff_t offset = start;
66
67         switch (iomap->type) {
68         case IOMAP_HOLE:
69                 return length;
70         case IOMAP_UNWRITTEN:
71                 offset = mapping_seek_hole_data(inode->i_mapping, start,
72                                 start + length, SEEK_DATA);
73                 if (offset < 0)
74                         return length;
75                 fallthrough;
76         default:
77                 *(loff_t *)data = offset;
78                 return 0;
79         }
80 }
81
82 loff_t
83 iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
84 {
85         loff_t size = i_size_read(inode);
86         loff_t length = size - offset;
87         loff_t ret;
88
89         /* Nothing to be found before or beyond the end of the file. */
90         if (offset < 0 || offset >= size)
91                 return -ENXIO;
92
93         while (length > 0) {
94                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
95                                   &offset, iomap_seek_data_actor);
96                 if (ret < 0)
97                         return ret;
98                 if (ret == 0)
99                         break;
100
101                 offset += ret;
102                 length -= ret;
103         }
104
105         if (length <= 0)
106                 return -ENXIO;
107         return offset;
108 }
109 EXPORT_SYMBOL_GPL(iomap_seek_data);