summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_file.c')
-rw-r--r--fs/xfs/xfs_file.c37
1 files changed, 36 insertions, 1 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 1b007f1ec18b..cede59a689e2 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -970,7 +970,8 @@ static inline bool xfs_file_sync_writes(struct file *filp)
#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(
@@ -1085,6 +1086,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 {
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode)) {