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