Merge tag 'oprofile-removal-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / fs / iomap / fiemap.c
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 #include <linux/fiemap.h>
10
11 struct fiemap_ctx {
12         struct fiemap_extent_info *fi;
13         struct iomap prev;
14 };
15
16 static int iomap_to_fiemap(struct fiemap_extent_info *fi,
17                 struct iomap *iomap, u32 flags)
18 {
19         switch (iomap->type) {
20         case IOMAP_HOLE:
21                 /* skip holes */
22                 return 0;
23         case IOMAP_DELALLOC:
24                 flags |= FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN;
25                 break;
26         case IOMAP_MAPPED:
27                 break;
28         case IOMAP_UNWRITTEN:
29                 flags |= FIEMAP_EXTENT_UNWRITTEN;
30                 break;
31         case IOMAP_INLINE:
32                 flags |= FIEMAP_EXTENT_DATA_INLINE;
33                 break;
34         }
35
36         if (iomap->flags & IOMAP_F_MERGED)
37                 flags |= FIEMAP_EXTENT_MERGED;
38         if (iomap->flags & IOMAP_F_SHARED)
39                 flags |= FIEMAP_EXTENT_SHARED;
40
41         return fiemap_fill_next_extent(fi, iomap->offset,
42                         iomap->addr != IOMAP_NULL_ADDR ? iomap->addr : 0,
43                         iomap->length, flags);
44 }
45
46 static loff_t
47 iomap_fiemap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
48                 struct iomap *iomap, struct iomap *srcmap)
49 {
50         struct fiemap_ctx *ctx = data;
51         loff_t ret = length;
52
53         if (iomap->type == IOMAP_HOLE)
54                 return length;
55
56         ret = iomap_to_fiemap(ctx->fi, &ctx->prev, 0);
57         ctx->prev = *iomap;
58         switch (ret) {
59         case 0:         /* success */
60                 return length;
61         case 1:         /* extent array full */
62                 return 0;
63         default:
64                 return ret;
65         }
66 }
67
68 int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
69                 u64 start, u64 len, const struct iomap_ops *ops)
70 {
71         struct fiemap_ctx ctx;
72         loff_t ret;
73
74         memset(&ctx, 0, sizeof(ctx));
75         ctx.fi = fi;
76         ctx.prev.type = IOMAP_HOLE;
77
78         ret = fiemap_prep(inode, fi, start, &len, 0);
79         if (ret)
80                 return ret;
81
82         while (len > 0) {
83                 ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx,
84                                 iomap_fiemap_actor);
85                 /* inode with no (attribute) mapping will give ENOENT */
86                 if (ret == -ENOENT)
87                         break;
88                 if (ret < 0)
89                         return ret;
90                 if (ret == 0)
91                         break;
92
93                 start += ret;
94                 len -= ret;
95         }
96
97         if (ctx.prev.type != IOMAP_HOLE) {
98                 ret = iomap_to_fiemap(fi, &ctx.prev, FIEMAP_EXTENT_LAST);
99                 if (ret < 0)
100                         return ret;
101         }
102
103         return 0;
104 }
105 EXPORT_SYMBOL_GPL(iomap_fiemap);
106
107 static loff_t
108 iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length,
109                 void *data, struct iomap *iomap, struct iomap *srcmap)
110 {
111         sector_t *bno = data, addr;
112
113         if (iomap->type == IOMAP_MAPPED) {
114                 addr = (pos - iomap->offset + iomap->addr) >> inode->i_blkbits;
115                 *bno = addr;
116         }
117         return 0;
118 }
119
120 /* legacy ->bmap interface.  0 is the error return (!) */
121 sector_t
122 iomap_bmap(struct address_space *mapping, sector_t bno,
123                 const struct iomap_ops *ops)
124 {
125         struct inode *inode = mapping->host;
126         loff_t pos = bno << inode->i_blkbits;
127         unsigned blocksize = i_blocksize(inode);
128         int ret;
129
130         if (filemap_write_and_wait(mapping))
131                 return 0;
132
133         bno = 0;
134         ret = iomap_apply(inode, pos, blocksize, 0, ops, &bno,
135                           iomap_bmap_actor);
136         if (ret)
137                 return 0;
138         return bno;
139 }
140 EXPORT_SYMBOL_GPL(iomap_bmap);