fs: move fiemap range validation into the file systems instances

Replace fiemap_check_flags with a fiemap_prep helper that also takes the
inode and mapped range, and performs the sanity check and truncation
previously done in fiemap_check_range.  This way the validation is inside
the file system itself and thus properly works for the stacked overlayfs
case as well.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Link: https://lore.kernel.org/r/20200523073016.2944131-7-hch@lst.de
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
Christoph Hellwig 2020-05-23 09:30:13 +02:00 committed by Theodore Ts'o
parent 2732881894
commit cddf8a2c4a
10 changed files with 47 additions and 54 deletions

View file

@ -203,16 +203,18 @@ EINTR once fatal signal received.
Flag checking should be done at the beginning of the ->fiemap callback via the Flag checking should be done at the beginning of the ->fiemap callback via the
fiemap_check_flags() helper: fiemap_prep() helper:
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags);
The struct fieinfo should be passed in as received from ioctl_fiemap(). The The struct fieinfo should be passed in as received from ioctl_fiemap(). The
set of fiemap flags which the fs understands should be passed via fs_flags. If set of fiemap flags which the fs understands should be passed via fs_flags. If
fiemap_check_flags finds invalid user flags, it will place the bad values in fiemap_prep finds invalid user flags, it will place the bad values in
fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from
fiemap_check_flags(), it should immediately exit, returning that error back to fiemap_prep(), it should immediately exit, returning that error back to
ioctl_fiemap(). ioctl_fiemap(). Additionally the range is validate against the supported
maximum file size.
For each extent in the request range, the file system should call For each extent in the request range, the file system should call

View file

@ -8250,7 +8250,7 @@ static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
{ {
int ret; int ret;
ret = fiemap_check_flags(fieinfo, BTRFS_FIEMAP_FLAGS); ret = fiemap_prep(inode, fieinfo, start, &len, BTRFS_FIEMAP_FLAGS);
if (ret) if (ret)
return ret; return ret;

View file

@ -3408,8 +3408,10 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
int i, num, rc, flags, last_blob; int i, num, rc, flags, last_blob;
u64 next; u64 next;
if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC)) rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len,
return -EBADR; FIEMAP_FLAG_SYNC);
if (rc)
return rc;
xid = get_xid(); xid = get_xid();
again: again:

View file

@ -4938,8 +4938,9 @@ int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo,
fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE; fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;
} }
if (fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)) error = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
return -EBADR; if (error)
return error;
error = ext4_fiemap_check_ranges(inode, start, &len); error = ext4_fiemap_check_ranges(inode, start, &len);
if (error) if (error)

View file

@ -1825,7 +1825,8 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
return ret; return ret;
} }
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR); ret = fiemap_prep(inode, fieinfo, start, &len,
FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR);
if (ret) if (ret)
return ret; return ret;

View file

@ -149,61 +149,50 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
EXPORT_SYMBOL(fiemap_fill_next_extent); EXPORT_SYMBOL(fiemap_fill_next_extent);
/** /**
* fiemap_check_flags - check validity of requested flags for fiemap * fiemap_prep - check validity of requested flags for fiemap
* @inode: Inode to operate on
* @fieinfo: Fiemap context passed into ->fiemap * @fieinfo: Fiemap context passed into ->fiemap
* @fs_flags: Set of fiemap flags that the file system understands * @start: Start of the mapped range
* @len: Length of the mapped range, can be truncated by this function.
* @supported_flags: Set of fiemap flags that the file system understands
* *
* Called from file system ->fiemap callback. This will compute the * This function must be called from each ->fiemap instance to validate the
* intersection of valid fiemap flags and those that the fs supports. That * fiemap request against the file system parameters.
* value is then compared against the user supplied flags. In case of bad user
* flags, the invalid values will be written into the fieinfo structure, and
* -EBADR is returned, which tells ioctl_fiemap() to return those values to
* userspace. For this reason, a return code of -EBADR should be preserved.
* *
* Returns 0 on success, -EBADR on bad flags. * Returns 0 on success, or a negative error on failure.
*/ */
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags) int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags)
{ {
u64 maxbytes = inode->i_sb->s_maxbytes;
u32 incompat_flags; u32 incompat_flags;
incompat_flags = fieinfo->fi_flags & ~(FIEMAP_FLAGS_COMPAT & fs_flags); if (*len == 0)
if (incompat_flags) {
fieinfo->fi_flags = incompat_flags;
return -EBADR;
}
return 0;
}
EXPORT_SYMBOL(fiemap_check_flags);
static int fiemap_check_ranges(struct super_block *sb,
u64 start, u64 len, u64 *new_len)
{
u64 maxbytes = (u64) sb->s_maxbytes;
*new_len = len;
if (len == 0)
return -EINVAL; return -EINVAL;
if (start > maxbytes) if (start > maxbytes)
return -EFBIG; return -EFBIG;
/* /*
* Shrink request scope to what the fs can actually handle. * Shrink request scope to what the fs can actually handle.
*/ */
if (len > maxbytes || (maxbytes - len) < start) if (*len > maxbytes || (maxbytes - *len) < start)
*new_len = maxbytes - start; *len = maxbytes - start;
supported_flags &= FIEMAP_FLAGS_COMPAT;
incompat_flags = fieinfo->fi_flags & ~supported_flags;
if (incompat_flags) {
fieinfo->fi_flags = incompat_flags;
return -EBADR;
}
return 0; return 0;
} }
EXPORT_SYMBOL(fiemap_prep);
static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap) static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
{ {
struct fiemap fiemap; struct fiemap fiemap;
struct fiemap_extent_info fieinfo = { 0, }; struct fiemap_extent_info fieinfo = { 0, };
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct super_block *sb = inode->i_sb;
u64 len;
int error; int error;
if (!inode->i_op->fiemap) if (!inode->i_op->fiemap)
@ -215,11 +204,6 @@ static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
return -EINVAL; return -EINVAL;
error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
&len);
if (error)
return error;
fieinfo.fi_flags = fiemap.fm_flags; fieinfo.fi_flags = fiemap.fm_flags;
fieinfo.fi_extents_max = fiemap.fm_extent_count; fieinfo.fi_extents_max = fiemap.fm_extent_count;
fieinfo.fi_extents_start = ufiemap->fm_extents; fieinfo.fi_extents_start = ufiemap->fm_extents;
@ -232,7 +216,8 @@ static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(inode->i_mapping); filemap_write_and_wait(inode->i_mapping);
error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len); error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start,
fiemap.fm_length);
fiemap.fm_flags = fieinfo.fi_flags; fiemap.fm_flags = fieinfo.fi_flags;
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap))) if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
@ -320,7 +305,7 @@ static int __generic_block_fiemap(struct inode *inode,
bool past_eof = false, whole_file = false; bool past_eof = false, whole_file = false;
int ret = 0; int ret = 0;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
if (ret) if (ret)
return ret; return ret;

View file

@ -75,7 +75,7 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
ctx.fi = fi; ctx.fi = fi;
ctx.prev.type = IOMAP_HOLE; ctx.prev.type = IOMAP_HOLE;
ret = fiemap_check_flags(fi, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fi, start, &len, FIEMAP_FLAG_SYNC);
if (ret) if (ret)
return ret; return ret;

View file

@ -1006,7 +1006,7 @@ int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
unsigned int blkbits = inode->i_blkbits; unsigned int blkbits = inode->i_blkbits;
int ret, n; int ret, n;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
if (ret) if (ret)
return ret; return ret;

View file

@ -746,7 +746,8 @@ int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
struct buffer_head *di_bh = NULL; struct buffer_head *di_bh = NULL;
struct ocfs2_extent_rec rec; struct ocfs2_extent_rec rec;
ret = fiemap_check_flags(fieinfo, OCFS2_FIEMAP_FLAGS); ret = fiemap_prep(inode, fieinfo, map_start, &map_len,
OCFS2_FIEMAP_FLAGS);
if (ret) if (ret)
return ret; return ret;

View file

@ -13,9 +13,10 @@ struct fiemap_extent_info {
fiemap_extent array */ fiemap_extent array */
}; };
int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags);
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical, int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
u64 phys, u64 len, u32 flags); u64 phys, u64 len, u32 flags);
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
int generic_block_fiemap(struct inode *inode, int generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo, u64 start, u64 len, struct fiemap_extent_info *fieinfo, u64 start, u64 len,