mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
b75dfde121
We better warn the fibmap user and not return a truncated and therefore an incorrect block map address if the bmap() returned block address is greater than INT_MAX (since user supplied integer pointer). It's better to pr_warn() all user of ioctl_fibmap() and return a proper error code rather than silently letting a FS corruption happen if the user tries to fiddle around with the returned block map address. We fix this by returning an error code of -ERANGE and returning 0 as the block mapping address in case if it is > INT_MAX. Now iomap_bmap() could be called from either of these two paths. Either when a user is calling an ioctl_fibmap() interface to get the block mapping address or by some filesystem via use of bmap() internal kernel API. bmap() kernel API is well equipped with handling of u64 addresses. WARN condition in iomap_bmap_actor() was mainly added to warn all the fibmap users. But now that we have directly added this warning for all fibmap users and also made sure to return 0 as block map address in case if addr > INT_MAX. So we can now remove this logic from iomap_bmap_actor(). Signed-off-by: Ritesh Harjani <riteshh@linux.ibm.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
146 lines
3.0 KiB
C
146 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2016-2018 Christoph Hellwig.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/iomap.h>
|
|
|
|
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);
|