diff options
Diffstat (limited to 'fs/xfs/xfs_file.c')
-rw-r--r-- | fs/xfs/xfs_file.c | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index b93a1fb0d13d..6e165b9686c9 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -987,7 +987,8 @@ xfs_break_layouts( #define XFS_FALLOC_FL_SUPPORTED \ (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | \ - FALLOC_FL_INSERT_RANGE | FALLOC_FL_UNSHARE_RANGE) + FALLOC_FL_INSERT_RANGE | FALLOC_FL_UNSHARE_RANGE | \ + FALLOC_FL_MAP_FREE_SPACE) STATIC long xfs_file_fallocate( @@ -1099,6 +1100,40 @@ xfs_file_fallocate( goto out_unlock; } do_file_insert = true; + } else if (mode & FALLOC_FL_MAP_FREE_SPACE) { + struct xfs_mount *mp = ip->i_mount; + xfs_off_t device_size; + + if (!capable(CAP_SYS_ADMIN)) { + error = -EPERM; + goto out_unlock; + } + + if (XFS_IS_REALTIME_INODE(ip)) + device_size = XFS_FSB_TO_B(mp, mp->m_sb.sb_rblocks); + else + device_size = XFS_FSB_TO_B(mp, mp->m_sb.sb_dblocks); + + /* + * Bail out now if we aren't allowed to make the file size the + * same length as the device. + */ + if (device_size > i_size_read(inode)) { + new_size = device_size; + error = inode_newsize_ok(inode, new_size); + if (error) + goto out_unlock; + } + + if (XFS_IS_REALTIME_INODE(ip)) + error = xfs_map_free_rt_space(ip, offset, len); + else + error = xfs_map_free_space(ip, offset, len); + if (error) { + if (error == -ECANCELED) + error = 0; + goto out_unlock; + } } else { flags |= XFS_PREALLOC_SET; |