- diff --git a/fs/fcntl.c b/fs/fcntl.c
- index 5598e4d57422..52555e6f5a8b 100644
- --- a/fs/fcntl.c
- +++ b/fs/fcntl.c
- @@ -18,11 +18,14 @@
- #include <linux/module.h>
- #include <linux/pipe_fs_i.h>
- #include <linux/security.h>
- +#include <linux/falloc.h>
- #include <linux/ptrace.h>
- #include <linux/signal.h>
- #include <linux/rcupdate.h>
- #include <linux/pid_namespace.h>
- #include <linux/user_namespace.h>
- +#include <linux/fsnotify.h>
- +#include <linux/mnt_idmapping.h>
- #include <linux/memfd.h>
- #include <linux/compat.h>
- #include <linux/mount.h>
- @@ -36,6 +39,122 @@
- #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
- +static int flock_to_abs_range(struct file *file, struct flock *fl,
- + loff_t *start, loff_t *len)
- +{
- + struct inode *inode = file_inode(file);
- + loff_t base;
- +
- + if (!fl || !start || !len)
- + return -EINVAL;
- +
- + switch (fl->l_whence) {
- + case SEEK_SET:
- + base = 0;
- + break;
- + case SEEK_CUR:
- + base = file->f_pos;
- + break;
- + case SEEK_END:
- + base = i_size_read(inode);
- + break;
- + default:
- + return -EINVAL;
- + }
- +
- + /*
- + * Keep the implementation simple:
- + * l_len > 0 : fixed byte range
- + * l_len == 0: [start, EOF)
- + * l_len < 0 : reject for now
- + */
- + if (fl->l_len < 0)
- + return -EINVAL;
- +
- + if (check_add_overflow(base, (loff_t)fl->l_start, start))
- + return -EOVERFLOW;
- +
- + if (*start < 0)
- + return -EINVAL;
- +
- + *len = fl->l_len;
- + return 0;
- +}
- +
- +static int do_fcntl_allocsp(struct file *file, struct flock *fl)
- +{
- + loff_t start, len;
- + int error;
- +
- + error = flock_to_abs_range(file, fl, &start, &len);
- + if (error)
- + return error;
- +
- + /* Empty allocation request: NO-OP */
- + if (len == 0)
- + return 0;
- +
- + return vfs_fallocate(file, 0, start, len);
- +}
- +
- +static int do_fcntl_freesp(struct file *file, struct flock *fl)
- +{
- + struct inode *inode = file_inode(file);
- + struct mnt_idmap *idmap = file_mnt_idmap(file);
- + loff_t start, len, isize, end;
- + int error;
- +
- + error = flock_to_abs_range(file, fl, &start, &len);
- + if (error)
- + return error;
- +
- + isize = i_size_read(inode);
- +
- + if (start >= isize)
- + return 0;
- +
- + /*
- + * { Solaris, FreeBSD, SUPER/UX, ... }-style |l_len == 0| means
- + * "from start to EOF".
- + * Linux hole punching keeps |i_size| unchanged, so use truncate
- + * whenever we are freeing through EOF.
- + */
- + if (len == 0)
- + return do_truncate(idmap, file->f_path.dentry, start, 0, file);
- +
- + if (check_add_overflow(start, len, &end))
- + return -EOVERFLOW;
- +
- + if (end >= isize)
- + return do_truncate(idmap, file->f_path.dentry, start, 0, file);
- +
- + return vfs_fallocate(file,
- + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
- + start, len);
- +}
- +
- +static int do_fcntl_space(struct file *file, unsigned int cmd, unsigned long arg)
- +{
- + struct flock fl;
- +
- + if (copy_from_user(&fl, (void __user *)arg, sizeof(fl)))
- + return -EFAULT;
- +
- + /*
- + * Linux uses |fcntl()| record locking for byte ranges, but these
- + * commands are not locks; only the range fields are interpreted
- + * here.
- + */
- + switch (cmd) {
- + case F_ALLOCSP:
- + return do_fcntl_allocsp(file, &fl);
- + case F_FREESP:
- + return do_fcntl_freesp(file, &fl);
- + default:
- + return -EINVAL;
- + }
- +}
- +
- static int setfl(int fd, struct file * filp, unsigned int arg)
- {
- struct inode * inode = file_inode(filp);
- @@ -552,6 +671,13 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
- case F_SET_RW_HINT:
- err = fcntl_set_rw_hint(filp, cmd, arg);
- break;
- + case F_ALLOCSP:
- + case F_FREESP:
- + if (!(filp->f_mode & FMODE_WRITE))
- + err = -EBADF;
- + else
- + err = do_fcntl_space(filp, cmd, arg);
- + break;
- default:
- break;
- }
- diff --git a/fs/internal.h b/fs/internal.h
- index 38e8aab27bbd..178e564b1940 100644
- --- a/fs/internal.h
- +++ b/fs/internal.h
- @@ -59,6 +59,9 @@ int do_unlinkat(int dfd, struct filename *name);
- int may_linkat(struct mnt_idmap *idmap, const struct path *link);
- int do_renameat2(int olddfd, struct filename *oldname, int newdfd,
- struct filename *newname, unsigned int flags);
- +int do_truncate(struct mnt_idmap *idmap, struct dentry *dentry,
- + loff_t length, unsigned int time_attrs,
- + struct file *filp);
- int do_mkdirat(int dfd, struct filename *name, umode_t mode);
- int do_symlinkat(struct filename *from, int newdfd, struct filename *to);
- int do_linkat(int olddfd, struct filename *old, int newdfd,
- diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
- index f291ab4f94eb..6076cb439542 100644
- --- a/include/uapi/linux/fcntl.h
- +++ b/include/uapi/linux/fcntl.h
- @@ -61,6 +61,13 @@
- #define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13)
- #define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14)
- +/*
- + * { Solaris, FreeBSD, SUPER/UX, ... }-style sparse file space
- + * allocation/freeing
- + */
- +#define F_ALLOCSP (F_LINUX_SPECIFIC_BASE + 15)
- +#define F_FREESP (F_LINUX_SPECIFIC_BASE + 16)
- +
- /*
- * Valid hint values for F_{GET,SET}_RW_HINT. 0 is "not set", or can be
- * used to clear any hints previously set.
Linux kernel linux6_17_rc4_rt3_fnctl_allocfree20260624_002 fnctl() F_ALLOCSP/F_FREESP patch
Posted by Anonymous on Wed 24th Jun 2026 16:37
raw | new post
view followups (newest first): Linux kernel linux6_17_rc4_rt3_fnctl_allocfree20260630_003 fnctl() F_ALLOCSP/F_FREESP patch by Anonymous
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
nrubsig.kpaste.net RSS