125 lines
2.8 KiB
C
125 lines
2.8 KiB
C
|
// 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>
|
||
|
#include <linux/pagemap.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);
|