diff options
author | Darrick J. Wong <djwong@kernel.org> | 2022-07-14 11:16:23 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2022-11-09 19:08:16 -0800 |
commit | e936e495ae1b7cf83a33e9b530dfa903a30dfad6 (patch) | |
tree | f28737b2e7b35c8ff74eee3df5588caa1a92d506 /fs/xfs/xfs_file.c | |
parent | 59f8bca3c5e494751aaded276dc0366dc4b87a82 (diff) |
xfs: fallocate free space into a filedefrag-freespace_2022-11-09
Add a new fallocate mode to map free physical space into a file, at the
same file offset as if the file were a sparse image of the physical
device backing the filesystem. The intent here is to use this to
prototype a free space defragmentation tool.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
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 fad5affdffab..de8f7f31ef90 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -960,7 +960,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( @@ -1075,6 +1076,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)) { |