Skip to content

Commit 4acaddf

Browse files
Christoph Hellwigbrauner
authored andcommitted
xfs: refactor xfs_file_fallocate
Refactor xfs_file_fallocate into separate helpers for each mode, two factors for i_size handling and a single switch statement over the supported modes. Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://lore.kernel.org/r/20240827065123.1762168-7-hch@lst.de Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 72f4d52 commit 4acaddf

File tree

1 file changed

+208
-122
lines changed

1 file changed

+208
-122
lines changed

fs/xfs/xfs_file.c

Lines changed: 208 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,192 @@ static inline bool xfs_file_sync_writes(struct file *filp)
852852
return false;
853853
}
854854

855+
static int
856+
xfs_falloc_newsize(
857+
struct file *file,
858+
int mode,
859+
loff_t offset,
860+
loff_t len,
861+
loff_t *new_size)
862+
{
863+
struct inode *inode = file_inode(file);
864+
865+
if ((mode & FALLOC_FL_KEEP_SIZE) || offset + len <= i_size_read(inode))
866+
return 0;
867+
*new_size = offset + len;
868+
return inode_newsize_ok(inode, *new_size);
869+
}
870+
871+
static int
872+
xfs_falloc_setsize(
873+
struct file *file,
874+
loff_t new_size)
875+
{
876+
struct iattr iattr = {
877+
.ia_valid = ATTR_SIZE,
878+
.ia_size = new_size,
879+
};
880+
881+
if (!new_size)
882+
return 0;
883+
return xfs_vn_setattr_size(file_mnt_idmap(file), file_dentry(file),
884+
&iattr);
885+
}
886+
887+
static int
888+
xfs_falloc_collapse_range(
889+
struct file *file,
890+
loff_t offset,
891+
loff_t len)
892+
{
893+
struct inode *inode = file_inode(file);
894+
loff_t new_size = i_size_read(inode) - len;
895+
int error;
896+
897+
if (!xfs_is_falloc_aligned(XFS_I(inode), offset, len))
898+
return -EINVAL;
899+
900+
/*
901+
* There is no need to overlap collapse range with EOF, in which case it
902+
* is effectively a truncate operation
903+
*/
904+
if (offset + len >= i_size_read(inode))
905+
return -EINVAL;
906+
907+
error = xfs_collapse_file_space(XFS_I(inode), offset, len);
908+
if (error)
909+
return error;
910+
return xfs_falloc_setsize(file, new_size);
911+
}
912+
913+
static int
914+
xfs_falloc_insert_range(
915+
struct file *file,
916+
loff_t offset,
917+
loff_t len)
918+
{
919+
struct inode *inode = file_inode(file);
920+
loff_t isize = i_size_read(inode);
921+
int error;
922+
923+
if (!xfs_is_falloc_aligned(XFS_I(inode), offset, len))
924+
return -EINVAL;
925+
926+
/*
927+
* New inode size must not exceed ->s_maxbytes, accounting for
928+
* possible signed overflow.
929+
*/
930+
if (inode->i_sb->s_maxbytes - isize < len)
931+
return -EFBIG;
932+
933+
/* Offset should be less than i_size */
934+
if (offset >= isize)
935+
return -EINVAL;
936+
937+
error = xfs_falloc_setsize(file, isize + len);
938+
if (error)
939+
return error;
940+
941+
/*
942+
* Perform hole insertion now that the file size has been updated so
943+
* that if we crash during the operation we don't leave shifted extents
944+
* past EOF and hence losing access to the data that is contained within
945+
* them.
946+
*/
947+
return xfs_insert_file_space(XFS_I(inode), offset, len);
948+
}
949+
950+
/*
951+
* Punch a hole and prealloc the range. We use a hole punch rather than
952+
* unwritten extent conversion for two reasons:
953+
*
954+
* 1.) Hole punch handles partial block zeroing for us.
955+
* 2.) If prealloc returns ENOSPC, the file range is still zero-valued by
956+
* virtue of the hole punch.
957+
*/
958+
static int
959+
xfs_falloc_zero_range(
960+
struct file *file,
961+
int mode,
962+
loff_t offset,
963+
loff_t len)
964+
{
965+
struct inode *inode = file_inode(file);
966+
unsigned int blksize = i_blocksize(inode);
967+
loff_t new_size = 0;
968+
int error;
969+
970+
trace_xfs_zero_file_space(XFS_I(inode));
971+
972+
error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
973+
if (error)
974+
return error;
975+
976+
error = xfs_free_file_space(XFS_I(inode), offset, len);
977+
if (error)
978+
return error;
979+
980+
len = round_up(offset + len, blksize) - round_down(offset, blksize);
981+
offset = round_down(offset, blksize);
982+
error = xfs_alloc_file_space(XFS_I(inode), offset, len);
983+
if (error)
984+
return error;
985+
return xfs_falloc_setsize(file, new_size);
986+
}
987+
988+
static int
989+
xfs_falloc_unshare_range(
990+
struct file *file,
991+
int mode,
992+
loff_t offset,
993+
loff_t len)
994+
{
995+
struct inode *inode = file_inode(file);
996+
loff_t new_size = 0;
997+
int error;
998+
999+
error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
1000+
if (error)
1001+
return error;
1002+
1003+
error = xfs_reflink_unshare(XFS_I(inode), offset, len);
1004+
if (error)
1005+
return error;
1006+
1007+
error = xfs_alloc_file_space(XFS_I(inode), offset, len);
1008+
if (error)
1009+
return error;
1010+
return xfs_falloc_setsize(file, new_size);
1011+
}
1012+
1013+
static int
1014+
xfs_falloc_allocate_range(
1015+
struct file *file,
1016+
int mode,
1017+
loff_t offset,
1018+
loff_t len)
1019+
{
1020+
struct inode *inode = file_inode(file);
1021+
loff_t new_size = 0;
1022+
int error;
1023+
1024+
/*
1025+
* If always_cow mode we can't use preallocations and thus should not
1026+
* create them.
1027+
*/
1028+
if (xfs_is_always_cow_inode(XFS_I(inode)))
1029+
return -EOPNOTSUPP;
1030+
1031+
error = xfs_falloc_newsize(file, mode, offset, len, &new_size);
1032+
if (error)
1033+
return error;
1034+
1035+
error = xfs_alloc_file_space(XFS_I(inode), offset, len);
1036+
if (error)
1037+
return error;
1038+
return xfs_falloc_setsize(file, new_size);
1039+
}
1040+
8551041
#define XFS_FALLOC_FL_SUPPORTED \
8561042
(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \
8571043
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | \
@@ -868,8 +1054,6 @@ xfs_file_fallocate(
8681054
struct xfs_inode *ip = XFS_I(inode);
8691055
long error;
8701056
uint iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
871-
loff_t new_size = 0;
872-
bool do_file_insert = false;
8731057

8741058
if (!S_ISREG(inode->i_mode))
8751059
return -EINVAL;
@@ -894,129 +1078,31 @@ xfs_file_fallocate(
8941078
if (error)
8951079
goto out_unlock;
8961080

897-
if (mode & FALLOC_FL_PUNCH_HOLE) {
1081+
switch (mode & FALLOC_FL_MODE_MASK) {
1082+
case FALLOC_FL_PUNCH_HOLE:
8981083
error = xfs_free_file_space(ip, offset, len);
899-
if (error)
900-
goto out_unlock;
901-
} else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
902-
if (!xfs_is_falloc_aligned(ip, offset, len)) {
903-
error = -EINVAL;
904-
goto out_unlock;
905-
}
906-
907-
/*
908-
* There is no need to overlap collapse range with EOF,
909-
* in which case it is effectively a truncate operation
910-
*/
911-
if (offset + len >= i_size_read(inode)) {
912-
error = -EINVAL;
913-
goto out_unlock;
914-
}
915-
916-
new_size = i_size_read(inode) - len;
917-
918-
error = xfs_collapse_file_space(ip, offset, len);
919-
if (error)
920-
goto out_unlock;
921-
} else if (mode & FALLOC_FL_INSERT_RANGE) {
922-
loff_t isize = i_size_read(inode);
923-
924-
if (!xfs_is_falloc_aligned(ip, offset, len)) {
925-
error = -EINVAL;
926-
goto out_unlock;
927-
}
928-
929-
/*
930-
* New inode size must not exceed ->s_maxbytes, accounting for
931-
* possible signed overflow.
932-
*/
933-
if (inode->i_sb->s_maxbytes - isize < len) {
934-
error = -EFBIG;
935-
goto out_unlock;
936-
}
937-
new_size = isize + len;
938-
939-
/* Offset should be less than i_size */
940-
if (offset >= isize) {
941-
error = -EINVAL;
942-
goto out_unlock;
943-
}
944-
do_file_insert = true;
945-
} else {
946-
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
947-
offset + len > i_size_read(inode)) {
948-
new_size = offset + len;
949-
error = inode_newsize_ok(inode, new_size);
950-
if (error)
951-
goto out_unlock;
952-
}
953-
954-
if (mode & FALLOC_FL_ZERO_RANGE) {
955-
/*
956-
* Punch a hole and prealloc the range. We use a hole
957-
* punch rather than unwritten extent conversion for two
958-
* reasons:
959-
*
960-
* 1.) Hole punch handles partial block zeroing for us.
961-
* 2.) If prealloc returns ENOSPC, the file range is
962-
* still zero-valued by virtue of the hole punch.
963-
*/
964-
unsigned int blksize = i_blocksize(inode);
965-
966-
trace_xfs_zero_file_space(ip);
967-
968-
error = xfs_free_file_space(ip, offset, len);
969-
if (error)
970-
goto out_unlock;
971-
972-
len = round_up(offset + len, blksize) -
973-
round_down(offset, blksize);
974-
offset = round_down(offset, blksize);
975-
} else if (mode & FALLOC_FL_UNSHARE_RANGE) {
976-
error = xfs_reflink_unshare(ip, offset, len);
977-
if (error)
978-
goto out_unlock;
979-
} else {
980-
/*
981-
* If always_cow mode we can't use preallocations and
982-
* thus should not create them.
983-
*/
984-
if (xfs_is_always_cow_inode(ip)) {
985-
error = -EOPNOTSUPP;
986-
goto out_unlock;
987-
}
988-
}
989-
990-
error = xfs_alloc_file_space(ip, offset, len);
991-
if (error)
992-
goto out_unlock;
993-
}
994-
995-
/* Change file size if needed */
996-
if (new_size) {
997-
struct iattr iattr;
998-
999-
iattr.ia_valid = ATTR_SIZE;
1000-
iattr.ia_size = new_size;
1001-
error = xfs_vn_setattr_size(file_mnt_idmap(file),
1002-
file_dentry(file), &iattr);
1003-
if (error)
1004-
goto out_unlock;
1005-
}
1006-
1007-
/*
1008-
* Perform hole insertion now that the file size has been
1009-
* updated so that if we crash during the operation we don't
1010-
* leave shifted extents past EOF and hence losing access to
1011-
* the data that is contained within them.
1012-
*/
1013-
if (do_file_insert) {
1014-
error = xfs_insert_file_space(ip, offset, len);
1015-
if (error)
1016-
goto out_unlock;
1084+
break;
1085+
case FALLOC_FL_COLLAPSE_RANGE:
1086+
error = xfs_falloc_collapse_range(file, offset, len);
1087+
break;
1088+
case FALLOC_FL_INSERT_RANGE:
1089+
error = xfs_falloc_insert_range(file, offset, len);
1090+
break;
1091+
case FALLOC_FL_ZERO_RANGE:
1092+
error = xfs_falloc_zero_range(file, mode, offset, len);
1093+
break;
1094+
case FALLOC_FL_UNSHARE_RANGE:
1095+
error = xfs_falloc_unshare_range(file, mode, offset, len);
1096+
break;
1097+
case FALLOC_FL_ALLOCATE_RANGE:
1098+
error = xfs_falloc_allocate_range(file, mode, offset, len);
1099+
break;
1100+
default:
1101+
error = -EOPNOTSUPP;
1102+
break;
10171103
}
10181104

1019-
if (xfs_file_sync_writes(file))
1105+
if (!error && xfs_file_sync_writes(file))
10201106
error = xfs_log_force_inode(ip);
10211107

10221108
out_unlock:

0 commit comments

Comments
 (0)