Skip to content

Commit 1602bad

Browse files
jtlaytonbrauner
authored andcommitted
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>
1 parent 8b99f6a commit 1602bad

4 files changed

Lines changed: 76 additions & 5 deletions

File tree

fs/fcntl.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
445445
struct file *filp)
446446
{
447447
void __user *argp = (void __user *)arg;
448+
struct delegation deleg;
448449
int argi = (int)arg;
449450
struct flock flock;
450451
long err = -EINVAL;
@@ -550,6 +551,18 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
550551
case F_SET_RW_HINT:
551552
err = fcntl_set_rw_hint(filp, arg);
552553
break;
554+
case F_GETDELEG:
555+
if (copy_from_user(&deleg, argp, sizeof(deleg)))
556+
return -EFAULT;
557+
err = fcntl_getdeleg(filp, &deleg);
558+
if (!err && copy_to_user(argp, &deleg, sizeof(deleg)))
559+
return -EFAULT;
560+
break;
561+
case F_SETDELEG:
562+
if (copy_from_user(&deleg, argp, sizeof(deleg)))
563+
return -EFAULT;
564+
err = fcntl_setdeleg(fd, filp, &deleg);
565+
break;
553566
default:
554567
break;
555568
}

fs/locks.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,7 +1703,7 @@ EXPORT_SYMBOL(lease_get_mtime);
17031703
* XXX: sfr & willy disagree over whether F_INPROGRESS
17041704
* should be returned to userspace.
17051705
*/
1706-
int fcntl_getlease(struct file *filp)
1706+
static int __fcntl_getlease(struct file *filp, unsigned int flavor)
17071707
{
17081708
struct file_lease *fl;
17091709
struct inode *inode = file_inode(filp);
@@ -1719,7 +1719,8 @@ int fcntl_getlease(struct file *filp)
17191719
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
17201720
if (fl->c.flc_file != filp)
17211721
continue;
1722-
type = target_leasetype(fl);
1722+
if (fl->c.flc_flags & flavor)
1723+
type = target_leasetype(fl);
17231724
break;
17241725
}
17251726
spin_unlock(&ctx->flc_lock);
@@ -1730,6 +1731,19 @@ int fcntl_getlease(struct file *filp)
17301731
return type;
17311732
}
17321733

1734+
int fcntl_getlease(struct file *filp)
1735+
{
1736+
return __fcntl_getlease(filp, FL_LEASE);
1737+
}
1738+
1739+
int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
1740+
{
1741+
if (deleg->d_flags != 0 || deleg->__pad != 0)
1742+
return -EINVAL;
1743+
deleg->d_type = __fcntl_getlease(filp, FL_DELEG);
1744+
return 0;
1745+
}
1746+
17331747
/**
17341748
* check_conflicting_open - see if the given file points to an inode that has
17351749
* 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)
20392053
}
20402054
EXPORT_SYMBOL_GPL(vfs_setlease);
20412055

2042-
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg)
2056+
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, unsigned int flavor, int arg)
20432057
{
20442058
struct file_lease *fl;
20452059
struct fasync_struct *new;
20462060
int error;
20472061

2048-
fl = lease_alloc(filp, FL_LEASE, arg);
2062+
fl = lease_alloc(filp, flavor, arg);
20492063
if (IS_ERR(fl))
20502064
return PTR_ERR(fl);
20512065

@@ -2081,7 +2095,28 @@ int fcntl_setlease(unsigned int fd, struct file *filp, int arg)
20812095

20822096
if (arg == F_UNLCK)
20832097
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
2084-
return do_fcntl_add_lease(fd, filp, arg);
2098+
return do_fcntl_add_lease(fd, filp, FL_LEASE, arg);
2099+
}
2100+
2101+
/**
2102+
* fcntl_setdeleg - sets a delegation on an open file
2103+
* @fd: open file descriptor
2104+
* @filp: file pointer
2105+
* @deleg: delegation request from userland
2106+
*
2107+
* Call this fcntl to establish a delegation on the file.
2108+
* Note that you also need to call %F_SETSIG to
2109+
* receive a signal when the lease is broken.
2110+
*/
2111+
int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg)
2112+
{
2113+
/* For now, no flags are supported */
2114+
if (deleg->d_flags != 0 || deleg->__pad != 0)
2115+
return -EINVAL;
2116+
2117+
if (deleg->d_type == F_UNLCK)
2118+
return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp);
2119+
return do_fcntl_add_lease(fd, filp, FL_DELEG, deleg->d_type);
20852120
}
20862121

20872122
/**

include/linux/filelock.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ int fcntl_setlk64(unsigned int, struct file *, unsigned int,
159159

160160
int fcntl_setlease(unsigned int fd, struct file *filp, int arg);
161161
int fcntl_getlease(struct file *filp);
162+
int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg);
163+
int fcntl_getdeleg(struct file *filp, struct delegation *deleg);
162164

163165
static inline bool lock_is_unlock(struct file_lock *fl)
164166
{
@@ -278,6 +280,16 @@ static inline int fcntl_getlease(struct file *filp)
278280
return F_UNLCK;
279281
}
280282

283+
static inline int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg)
284+
{
285+
return -EINVAL;
286+
}
287+
288+
static inline int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
289+
{
290+
return -EINVAL;
291+
}
292+
281293
static inline bool lock_is_unlock(struct file_lock *fl)
282294
{
283295
return false;

include/uapi/linux/fcntl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@
7979
*/
8080
#define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET
8181

82+
/* Set/Get delegations */
83+
#define F_GETDELEG (F_LINUX_SPECIFIC_BASE + 15)
84+
#define F_SETDELEG (F_LINUX_SPECIFIC_BASE + 16)
85+
86+
/* Argument structure for F_GETDELEG and F_SETDELEG */
87+
struct delegation {
88+
uint32_t d_flags; /* Must be 0 */
89+
uint16_t d_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
90+
uint16_t __pad; /* Must be 0 */
91+
};
92+
8293
/*
8394
* Types of directory notifications that may be requested.
8495
*/

0 commit comments

Comments
 (0)