xref: /openbmc/linux/fs/iomap/seek.c (revision c4c3c32d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2017 Red Hat, Inc.
4  * Copyright (c) 2018-2021 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 iomap_seek_hole_iter(const struct iomap_iter *iter,
14 		loff_t *hole_pos)
15 {
16 	loff_t length = iomap_length(iter);
17 
18 	switch (iter->iomap.type) {
19 	case IOMAP_UNWRITTEN:
20 		*hole_pos = mapping_seek_hole_data(iter->inode->i_mapping,
21 				iter->pos, iter->pos + length, SEEK_HOLE);
22 		if (*hole_pos == iter->pos + length)
23 			return length;
24 		return 0;
25 	case IOMAP_HOLE:
26 		*hole_pos = iter->pos;
27 		return 0;
28 	default:
29 		return length;
30 	}
31 }
32 
33 loff_t
34 iomap_seek_hole(struct inode *inode, loff_t pos, const struct iomap_ops *ops)
35 {
36 	loff_t size = i_size_read(inode);
37 	struct iomap_iter iter = {
38 		.inode	= inode,
39 		.pos	= pos,
40 		.flags	= IOMAP_REPORT,
41 	};
42 	int ret;
43 
44 	/* Nothing to be found before or beyond the end of the file. */
45 	if (pos < 0 || pos >= size)
46 		return -ENXIO;
47 
48 	iter.len = size - pos;
49 	while ((ret = iomap_iter(&iter, ops)) > 0)
50 		iter.processed = iomap_seek_hole_iter(&iter, &pos);
51 	if (ret < 0)
52 		return ret;
53 	if (iter.len) /* found hole before EOF */
54 		return pos;
55 	return size;
56 }
57 EXPORT_SYMBOL_GPL(iomap_seek_hole);
58 
59 static loff_t iomap_seek_data_iter(const struct iomap_iter *iter,
60 		loff_t *hole_pos)
61 {
62 	loff_t length = iomap_length(iter);
63 
64 	switch (iter->iomap.type) {
65 	case IOMAP_HOLE:
66 		return length;
67 	case IOMAP_UNWRITTEN:
68 		*hole_pos = mapping_seek_hole_data(iter->inode->i_mapping,
69 				iter->pos, iter->pos + length, SEEK_DATA);
70 		if (*hole_pos < 0)
71 			return length;
72 		return 0;
73 	default:
74 		*hole_pos = iter->pos;
75 		return 0;
76 	}
77 }
78 
79 loff_t
80 iomap_seek_data(struct inode *inode, loff_t pos, const struct iomap_ops *ops)
81 {
82 	loff_t size = i_size_read(inode);
83 	struct iomap_iter iter = {
84 		.inode	= inode,
85 		.pos	= pos,
86 		.flags	= IOMAP_REPORT,
87 	};
88 	int ret;
89 
90 	/* Nothing to be found before or beyond the end of the file. */
91 	if (pos < 0 || pos >= size)
92 		return -ENXIO;
93 
94 	iter.len = size - pos;
95 	while ((ret = iomap_iter(&iter, ops)) > 0)
96 		iter.processed = iomap_seek_data_iter(&iter, &pos);
97 	if (ret < 0)
98 		return ret;
99 	if (iter.len) /* found data before EOF */
100 		return pos;
101 	/* We've reached the end of the file without finding data */
102 	return -ENXIO;
103 }
104 EXPORT_SYMBOL_GPL(iomap_seek_data);
105