xref: /openbmc/linux/fs/iomap/fiemap.c (revision cbdf59ad)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2016-2018 Christoph Hellwig.
4  */
5 #include <linux/module.h>
6 #include <linux/compiler.h>
7 #include <linux/fs.h>
8 #include <linux/iomap.h>
9 
10 struct fiemap_ctx {
11 	struct fiemap_extent_info *fi;
12 	struct iomap prev;
13 };
14 
15 static int iomap_to_fiemap(struct fiemap_extent_info *fi,
16 		struct iomap *iomap, u32 flags)
17 {
18 	switch (iomap->type) {
19 	case IOMAP_HOLE:
20 		/* skip holes */
21 		return 0;
22 	case IOMAP_DELALLOC:
23 		flags |= FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN;
24 		break;
25 	case IOMAP_MAPPED:
26 		break;
27 	case IOMAP_UNWRITTEN:
28 		flags |= FIEMAP_EXTENT_UNWRITTEN;
29 		break;
30 	case IOMAP_INLINE:
31 		flags |= FIEMAP_EXTENT_DATA_INLINE;
32 		break;
33 	}
34 
35 	if (iomap->flags & IOMAP_F_MERGED)
36 		flags |= FIEMAP_EXTENT_MERGED;
37 	if (iomap->flags & IOMAP_F_SHARED)
38 		flags |= FIEMAP_EXTENT_SHARED;
39 
40 	return fiemap_fill_next_extent(fi, iomap->offset,
41 			iomap->addr != IOMAP_NULL_ADDR ? iomap->addr : 0,
42 			iomap->length, flags);
43 }
44 
45 static loff_t
46 iomap_fiemap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
47 		struct iomap *iomap)
48 {
49 	struct fiemap_ctx *ctx = data;
50 	loff_t ret = length;
51 
52 	if (iomap->type == IOMAP_HOLE)
53 		return length;
54 
55 	ret = iomap_to_fiemap(ctx->fi, &ctx->prev, 0);
56 	ctx->prev = *iomap;
57 	switch (ret) {
58 	case 0:		/* success */
59 		return length;
60 	case 1:		/* extent array full */
61 		return 0;
62 	default:
63 		return ret;
64 	}
65 }
66 
67 int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
68 		loff_t start, loff_t len, const struct iomap_ops *ops)
69 {
70 	struct fiemap_ctx ctx;
71 	loff_t ret;
72 
73 	memset(&ctx, 0, sizeof(ctx));
74 	ctx.fi = fi;
75 	ctx.prev.type = IOMAP_HOLE;
76 
77 	ret = fiemap_check_flags(fi, FIEMAP_FLAG_SYNC);
78 	if (ret)
79 		return ret;
80 
81 	if (fi->fi_flags & FIEMAP_FLAG_SYNC) {
82 		ret = filemap_write_and_wait(inode->i_mapping);
83 		if (ret)
84 			return ret;
85 	}
86 
87 	while (len > 0) {
88 		ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx,
89 				iomap_fiemap_actor);
90 		/* inode with no (attribute) mapping will give ENOENT */
91 		if (ret == -ENOENT)
92 			break;
93 		if (ret < 0)
94 			return ret;
95 		if (ret == 0)
96 			break;
97 
98 		start += ret;
99 		len -= ret;
100 	}
101 
102 	if (ctx.prev.type != IOMAP_HOLE) {
103 		ret = iomap_to_fiemap(fi, &ctx.prev, FIEMAP_EXTENT_LAST);
104 		if (ret < 0)
105 			return ret;
106 	}
107 
108 	return 0;
109 }
110 EXPORT_SYMBOL_GPL(iomap_fiemap);
111 
112 static loff_t
113 iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length,
114 		void *data, struct iomap *iomap)
115 {
116 	sector_t *bno = data, addr;
117 
118 	if (iomap->type == IOMAP_MAPPED) {
119 		addr = (pos - iomap->offset + iomap->addr) >> inode->i_blkbits;
120 		if (addr > INT_MAX)
121 			WARN(1, "would truncate bmap result\n");
122 		else
123 			*bno = addr;
124 	}
125 	return 0;
126 }
127 
128 /* legacy ->bmap interface.  0 is the error return (!) */
129 sector_t
130 iomap_bmap(struct address_space *mapping, sector_t bno,
131 		const struct iomap_ops *ops)
132 {
133 	struct inode *inode = mapping->host;
134 	loff_t pos = bno << inode->i_blkbits;
135 	unsigned blocksize = i_blocksize(inode);
136 
137 	if (filemap_write_and_wait(mapping))
138 		return 0;
139 
140 	bno = 0;
141 	iomap_apply(inode, pos, blocksize, 0, ops, &bno, iomap_bmap_actor);
142 	return bno;
143 }
144 EXPORT_SYMBOL_GPL(iomap_bmap);
145