mirror of https://github.com/torvalds/linux.git
vfs: expose delegation support to userland
Now that support for recallable directory delegations is available, expose this functionality to userland with new F_SETDELEG and F_GETDELEG commands for fcntl(). Note that this also allows userland to request a FL_DELEG type lease on files too. Userland applications that do will get signalled when there are metadata changes in addition to just data changes (which is a limitation of FL_LEASE leases). These commands accept a new "struct delegation" argument that contains a flags field for future expansion. Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-17-52f3feebb2f2@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
8b99f6a8c1
commit
1602bad16d
13
fs/fcntl.c
13
fs/fcntl.c
|
|
@ -445,6 +445,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||||
struct file *filp)
|
struct file *filp)
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
|
struct delegation deleg;
|
||||||
int argi = (int)arg;
|
int argi = (int)arg;
|
||||||
struct flock flock;
|
struct flock flock;
|
||||||
long err = -EINVAL;
|
long err = -EINVAL;
|
||||||
|
|
@ -550,6 +551,18 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||||
case F_SET_RW_HINT:
|
case F_SET_RW_HINT:
|
||||||
err = fcntl_set_rw_hint(filp, arg);
|
err = fcntl_set_rw_hint(filp, arg);
|
||||||
break;
|
break;
|
||||||
|
case F_GETDELEG:
|
||||||
|
if (copy_from_user(&deleg, argp, sizeof(deleg)))
|
||||||
|
return -EFAULT;
|
||||||
|
err = fcntl_getdeleg(filp, &deleg);
|
||||||
|
if (!err && copy_to_user(argp, &deleg, sizeof(deleg)))
|
||||||
|
return -EFAULT;
|
||||||
|
break;
|
||||||
|
case F_SETDELEG:
|
||||||
|
if (copy_from_user(&deleg, argp, sizeof(deleg)))
|
||||||
|
return -EFAULT;
|
||||||
|
err = fcntl_setdeleg(fd, filp, &deleg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
fs/locks.c
45
fs/locks.c
|
|
@ -1703,7 +1703,7 @@ EXPORT_SYMBOL(lease_get_mtime);
|
||||||
* XXX: sfr & willy disagree over whether F_INPROGRESS
|
* XXX: sfr & willy disagree over whether F_INPROGRESS
|
||||||
* should be returned to userspace.
|
* should be returned to userspace.
|
||||||
*/
|
*/
|
||||||
int fcntl_getlease(struct file *filp)
|
static int __fcntl_getlease(struct file *filp, unsigned int flavor)
|
||||||
{
|
{
|
||||||
struct file_lease *fl;
|
struct file_lease *fl;
|
||||||
struct inode *inode = file_inode(filp);
|
struct inode *inode = file_inode(filp);
|
||||||
|
|
@ -1719,7 +1719,8 @@ int fcntl_getlease(struct file *filp)
|
||||||
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
|
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
|
||||||
if (fl->c.flc_file != filp)
|
if (fl->c.flc_file != filp)
|
||||||
continue;
|
continue;
|
||||||
type = target_leasetype(fl);
|
if (fl->c.flc_flags & flavor)
|
||||||
|
type = target_leasetype(fl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock(&ctx->flc_lock);
|
spin_unlock(&ctx->flc_lock);
|
||||||
|
|
@ -1730,6 +1731,19 @@ int fcntl_getlease(struct file *filp)
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fcntl_getlease(struct file *filp)
|
||||||
|
{
|
||||||
|
return __fcntl_getlease(filp, FL_LEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
|
||||||
|
{
|
||||||
|
if (deleg->d_flags != 0 || deleg->__pad != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
deleg->d_type = __fcntl_getlease(filp, FL_DELEG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check_conflicting_open - see if the given file points to an inode that has
|
* check_conflicting_open - see if the given file points to an inode that has
|
||||||
* an existing open that would conflict with the
|
* an existing open that would conflict with the
|
||||||
|
|
@ -2039,13 +2053,13 @@ vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vfs_setlease);
|
EXPORT_SYMBOL_GPL(vfs_setlease);
|
||||||
|
|
||||||
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg)
|
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, unsigned int flavor, int arg)
|
||||||
{
|
{
|
||||||
struct file_lease *fl;
|
struct file_lease *fl;
|
||||||
struct fasync_struct *new;
|
struct fasync_struct *new;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
fl = lease_alloc(filp, FL_LEASE, arg);
|
fl = lease_alloc(filp, flavor, arg);
|
||||||
if (IS_ERR(fl))
|
if (IS_ERR(fl))
|
||||||
return PTR_ERR(fl);
|
return PTR_ERR(fl);
|
||||||
|
|
||||||
|
|
@ -2081,7 +2095,28 @@ int fcntl_setlease(unsigned int fd, struct file *filp, int arg)
|
||||||
|
|
||||||
if (arg == F_UNLCK)
|
if (arg == F_UNLCK)
|
||||||
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
|
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
|
||||||
return do_fcntl_add_lease(fd, filp, arg);
|
return do_fcntl_add_lease(fd, filp, FL_LEASE, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fcntl_setdeleg - sets a delegation on an open file
|
||||||
|
* @fd: open file descriptor
|
||||||
|
* @filp: file pointer
|
||||||
|
* @deleg: delegation request from userland
|
||||||
|
*
|
||||||
|
* Call this fcntl to establish a delegation on the file.
|
||||||
|
* Note that you also need to call %F_SETSIG to
|
||||||
|
* receive a signal when the lease is broken.
|
||||||
|
*/
|
||||||
|
int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg)
|
||||||
|
{
|
||||||
|
/* For now, no flags are supported */
|
||||||
|
if (deleg->d_flags != 0 || deleg->__pad != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (deleg->d_type == F_UNLCK)
|
||||||
|
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
|
||||||
|
return do_fcntl_add_lease(fd, filp, FL_DELEG, deleg->d_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,8 @@ int fcntl_setlk64(unsigned int, struct file *, unsigned int,
|
||||||
|
|
||||||
int fcntl_setlease(unsigned int fd, struct file *filp, int arg);
|
int fcntl_setlease(unsigned int fd, struct file *filp, int arg);
|
||||||
int fcntl_getlease(struct file *filp);
|
int fcntl_getlease(struct file *filp);
|
||||||
|
int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg);
|
||||||
|
int fcntl_getdeleg(struct file *filp, struct delegation *deleg);
|
||||||
|
|
||||||
static inline bool lock_is_unlock(struct file_lock *fl)
|
static inline bool lock_is_unlock(struct file_lock *fl)
|
||||||
{
|
{
|
||||||
|
|
@ -278,6 +280,16 @@ static inline int fcntl_getlease(struct file *filp)
|
||||||
return F_UNLCK;
|
return F_UNLCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool lock_is_unlock(struct file_lock *fl)
|
static inline bool lock_is_unlock(struct file_lock *fl)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,17 @@
|
||||||
*/
|
*/
|
||||||
#define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET
|
#define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET
|
||||||
|
|
||||||
|
/* Set/Get delegations */
|
||||||
|
#define F_GETDELEG (F_LINUX_SPECIFIC_BASE + 15)
|
||||||
|
#define F_SETDELEG (F_LINUX_SPECIFIC_BASE + 16)
|
||||||
|
|
||||||
|
/* Argument structure for F_GETDELEG and F_SETDELEG */
|
||||||
|
struct delegation {
|
||||||
|
uint32_t d_flags; /* Must be 0 */
|
||||||
|
uint16_t d_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
|
||||||
|
uint16_t __pad; /* Must be 0 */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types of directory notifications that may be requested.
|
* Types of directory notifications that may be requested.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue