xref: /openbmc/linux/fs/iomap/seek.c (revision 2f61c664)
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);
110