xref: /openbmc/linux/fs/iomap/seek.c (revision ac4dfccb)
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 ret;
39 
40 	/* Nothing to be found before or beyond the end of the file. */
41 	if (offset < 0 || offset >= size)
42 		return -ENXIO;
43 
44 	while (offset < size) {
45 		ret = iomap_apply(inode, offset, size - offset, IOMAP_REPORT,
46 				  ops, &offset, iomap_seek_hole_actor);
47 		if (ret < 0)
48 			return ret;
49 		if (ret == 0)
50 			break;
51 		offset += ret;
52 	}
53 
54 	return offset;
55 }
56 EXPORT_SYMBOL_GPL(iomap_seek_hole);
57 
58 static loff_t
59 iomap_seek_data_actor(struct inode *inode, loff_t start, loff_t length,
60 		      void *data, struct iomap *iomap, struct iomap *srcmap)
61 {
62 	loff_t offset = start;
63 
64 	switch (iomap->type) {
65 	case IOMAP_HOLE:
66 		return length;
67 	case IOMAP_UNWRITTEN:
68 		offset = mapping_seek_hole_data(inode->i_mapping, start,
69 				start + length, SEEK_DATA);
70 		if (offset < 0)
71 			return length;
72 		fallthrough;
73 	default:
74 		*(loff_t *)data = offset;
75 		return 0;
76 	}
77 }
78 
79 loff_t
80 iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
81 {
82 	loff_t size = i_size_read(inode);
83 	loff_t ret;
84 
85 	/* Nothing to be found before or beyond the end of the file. */
86 	if (offset < 0 || offset >= size)
87 		return -ENXIO;
88 
89 	while (offset < size) {
90 		ret = iomap_apply(inode, offset, size - offset, IOMAP_REPORT,
91 				  ops, &offset, iomap_seek_data_actor);
92 		if (ret < 0)
93 			return ret;
94 		if (ret == 0)
95 			return offset;
96 		offset += ret;
97 	}
98 
99 	/* We've reached the end of the file without finding data */
100 	return -ENXIO;
101 }
102 EXPORT_SYMBOL_GPL(iomap_seek_data);
103