// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2018 Christoph Hellwig. */ #include #include #include #include #include struct fiemap_ctx { struct fiemap_extent_info *fi; struct iomap prev; }; static int iomap_to_fiemap(struct fiemap_extent_info *fi, struct iomap *iomap, u32 flags) { switch (iomap->type) { case IOMAP_HOLE: /* skip holes */ return 0; case IOMAP_DELALLOC: flags |= FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN; break; case IOMAP_MAPPED: break; case IOMAP_UNWRITTEN: flags |= FIEMAP_EXTENT_UNWRITTEN; break; case IOMAP_INLINE: flags |= FIEMAP_EXTENT_DATA_INLINE; break; } if (iomap->flags & IOMAP_F_MERGED) flags |= FIEMAP_EXTENT_MERGED; if (iomap->flags & IOMAP_F_SHARED) flags |= FIEMAP_EXTENT_SHARED; return fiemap_fill_next_extent(fi, iomap->offset, iomap->addr != IOMAP_NULL_ADDR ? iomap->addr : 0, iomap->length, flags); } static loff_t iomap_fiemap_actor(struct inode *inode, loff_t pos, loff_t length, void *data, struct iomap *iomap, struct iomap *srcmap) { struct fiemap_ctx *ctx = data; loff_t ret = length; if (iomap->type == IOMAP_HOLE) return length; ret = iomap_to_fiemap(ctx->fi, &ctx->prev, 0); ctx->prev = *iomap; switch (ret) { case 0: /* success */ return length; case 1: /* extent array full */ return 0; default: return ret; } } int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi, loff_t start, loff_t len, const struct iomap_ops *ops) { struct fiemap_ctx ctx; loff_t ret; memset(&ctx, 0, sizeof(ctx)); ctx.fi = fi; ctx.prev.type = IOMAP_HOLE; ret = fiemap_check_flags(fi, FIEMAP_FLAG_SYNC); if (ret) return ret; if (fi->fi_flags & FIEMAP_FLAG_SYNC) { ret = filemap_write_and_wait(inode->i_mapping); if (ret) return ret; } while (len > 0) { ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx, iomap_fiemap_actor); /* inode with no (attribute) mapping will give ENOENT */ if (ret == -ENOENT) break; if (ret < 0) return ret; if (ret == 0) break; start += ret; len -= ret; } if (ctx.prev.type != IOMAP_HOLE) { ret = iomap_to_fiemap(fi, &ctx.prev, FIEMAP_EXTENT_LAST); if (ret < 0) return ret; } return 0; } EXPORT_SYMBOL_GPL(iomap_fiemap); static loff_t iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length, void *data, struct iomap *iomap, struct iomap *srcmap) { sector_t *bno = data, addr; if (iomap->type == IOMAP_MAPPED) { addr = (pos - iomap->offset + iomap->addr) >> inode->i_blkbits; *bno = addr; } return 0; } /* legacy ->bmap interface. 0 is the error return (!) */ sector_t iomap_bmap(struct address_space *mapping, sector_t bno, const struct iomap_ops *ops) { struct inode *inode = mapping->host; loff_t pos = bno << inode->i_blkbits; unsigned blocksize = i_blocksize(inode); int ret; if (filemap_write_and_wait(mapping)) return 0; bno = 0; ret = iomap_apply(inode, pos, blocksize, 0, ops, &bno, iomap_bmap_actor); if (ret) return 0; return bno; } EXPORT_SYMBOL_GPL(iomap_bmap);