From c348433ae24de39b6075f699fde500a3ea04efe3 Mon Sep 17 00:00:00 2001 From: Tim Chase Date: Tue, 15 Jul 2014 13:58:28 -0500 Subject: [PATCH] Linux semantics for fallocate() According to the Linux documentation, the FALLOC_FL_KEEP_SIZE flag must be set in conjunction with the FALLOC_FL_PUNCH_HOLE flag. The previous logic caused a failure whenever FALLOC_FL_KEEP_SIZE was present. Also, mimic the behavior of other native file systems such as ext4 in cases where the file might be extended. If the offset is beyond the end of the file, return success without changing the file. If the extent of the punched hole would extend the file, only the existing tail of the file is punched. References: https://git.kernel.org/cgit/docs/man-pages/man-pages.git/tree/man2/fallocate.2#n102 --- module/zfs/zpl_file.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c index 2a7bcb9b0a69..10f8d8ff14fe 100644 --- a/module/zfs/zpl_file.c +++ b/module/zfs/zpl_file.c @@ -486,7 +486,11 @@ zpl_fallocate_common(struct inode *ip, int mode, loff_t offset, loff_t len) cred_t *cr = CRED(); int error = -EOPNOTSUPP; +#ifdef FALLOC_FL_PUNCH_HOLE + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) +#else if (mode & FALLOC_FL_KEEP_SIZE) +#endif return (-EOPNOTSUPP); crhold(cr); @@ -494,7 +498,13 @@ zpl_fallocate_common(struct inode *ip, int mode, loff_t offset, loff_t len) #ifdef FALLOC_FL_PUNCH_HOLE if (mode & FALLOC_FL_PUNCH_HOLE) { flock64_t bf; + loff_t olen; + olen = i_size_read(ip); + if (offset > olen) + return (0); + if (offset + len > olen) + len = olen - offset; bf.l_type = F_WRLCK; bf.l_whence = 0; bf.l_start = offset; @@ -502,6 +512,9 @@ zpl_fallocate_common(struct inode *ip, int mode, loff_t offset, loff_t len) bf.l_pid = 0; error = -zfs_space(ip, F_FREESP, &bf, FWRITE, offset, cr); + if (!error) + truncate_inode_pages_range(ip->i_mapping, offset, + offset + len - 1); } #endif /* FALLOC_FL_PUNCH_HOLE */