text: // SPDX-License-Identifier: GPL-2.0
  /*
   * Copyright (c) 2016-2021 Christoph Hellwig.
   */
  #include <linux/module.h>
  #include <linux/compiler.h>
  #include <linux/fs.h>
  #include <linux/iomap.h>
  #include <linux/fiemap.h>
  
  static int iomap_to_fiemap(struct fiemap_extent_info *fi,
  		const 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_iter(const struct iomap_iter *iter,
  		struct fiemap_extent_info *fi, struct iomap *prev)
  {
  	int ret;
  
  	if (iter->iomap.type == IOMAP_HOLE)
  		return iomap_length(iter);
  
  	ret = iomap_to_fiemap(fi, prev, 0);
  	*prev = iter->iomap;
  	switch (ret) {
  	case 0:		/* success */
  		return iomap_length(iter);
  	case 1:		/* extent array full */
  		return 0;
  	default:	/* error */
  		return ret;
  	}
  }
  
  int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
  		u64 start, u64 len, const struct iomap_ops *ops)
  {
  	struct iomap_iter iter = {
  		.inode		= inode,
  		.pos		= start,
  		.len		= len,
  		.flags		= IOMAP_REPORT,
  	};
  	struct iomap prev = {
  		.type		= IOMAP_HOLE,
  	};
  	int ret;
  
  	ret = fiemap_prep(inode, fi, start, &iter.len, 0);
  	if (ret)
  		return ret;
  
  	while ((ret = iomap_iter(&iter, ops)) > 0)
  		iter.processed = iomap_fiemap_iter(&iter, fi, &prev);
  
  	if (prev.type != IOMAP_HOLE) {
  		ret = iomap_to_fiemap(fi, &prev, FIEMAP_EXTENT_LAST);
  		if (ret < 0)
  			return ret;
  	}
  
  	/* inode with no (attribute) mapping will give ENOENT */
  	if (ret < 0 && ret != -ENOENT)
  		return ret;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(iomap_fiemap);
  
  /* 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 iomap_iter iter = {
  		.inode	= mapping->host,
  		.pos	= (loff_t)bno << mapping->host->i_blkbits,
  		.len	= i_blocksize(mapping->host),
  		.flags	= IOMAP_REPORT,
  	};
  	const unsigned int blkshift = mapping->host->i_blkbits - SECTOR_SHIFT;
  	int ret;
  
  	if (filemap_write_and_wait(mapping))
  		return 0;
  
  	bno = 0;
  	while ((ret = iomap_iter(&iter, ops)) > 0) {
  		if (iter.iomap.type == IOMAP_MAPPED)
  			bno = iomap_sector(&iter.iomap, iter.pos) >> blkshift;
  		/* leave iter.processed unset to abort loop */
  	}
  	if (ret)
  		return 0;
  
  	return bno;
  }
  EXPORT_SYMBOL_GPL(iomap_bmap);
  