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
13static 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
33loff_t
34iomap_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}
57EXPORT_SYMBOL_GPL(iomap_seek_hole);
58
59static 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
79loff_t
80iomap_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}
104EXPORT_SYMBOL_GPL(iomap_seek_data);
105