Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Jeff Layton and committed by
Christian Brauner
1602bad1 8b99f6a8

+76 -5
+13
fs/fcntl.c
··· 445 445 struct file *filp) 446 446 { 447 447 void __user *argp = (void __user *)arg; 448 + struct delegation deleg; 448 449 int argi = (int)arg; 449 450 struct flock flock; 450 451 long err = -EINVAL; ··· 550 549 break; 551 550 case F_SET_RW_HINT: 552 551 err = fcntl_set_rw_hint(filp, arg); 552 + break; 553 + case F_GETDELEG: 554 + if (copy_from_user(&deleg, argp, sizeof(deleg))) 555 + return -EFAULT; 556 + err = fcntl_getdeleg(filp, &deleg); 557 + if (!err && copy_to_user(argp, &deleg, sizeof(deleg))) 558 + return -EFAULT; 559 + break; 560 + case F_SETDELEG: 561 + if (copy_from_user(&deleg, argp, sizeof(deleg))) 562 + return -EFAULT; 563 + err = fcntl_setdeleg(fd, filp, &deleg); 553 564 break; 554 565 default: 555 566 break;
+40 -5
fs/locks.c
··· 1703 1703 * XXX: sfr & willy disagree over whether F_INPROGRESS 1704 1704 * should be returned to userspace. 1705 1705 */ 1706 - int fcntl_getlease(struct file *filp) 1706 + static int __fcntl_getlease(struct file *filp, unsigned int flavor) 1707 1707 { 1708 1708 struct file_lease *fl; 1709 1709 struct inode *inode = file_inode(filp); ··· 1719 1719 list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) { 1720 1720 if (fl->c.flc_file != filp) 1721 1721 continue; 1722 - type = target_leasetype(fl); 1722 + if (fl->c.flc_flags & flavor) 1723 + type = target_leasetype(fl); 1723 1724 break; 1724 1725 } 1725 1726 spin_unlock(&ctx->flc_lock); ··· 1729 1728 locks_dispose_list(&dispose); 1730 1729 } 1731 1730 return type; 1731 + } 1732 + 1733 + int fcntl_getlease(struct file *filp) 1734 + { 1735 + return __fcntl_getlease(filp, FL_LEASE); 1736 + } 1737 + 1738 + int fcntl_getdeleg(struct file *filp, struct delegation *deleg) 1739 + { 1740 + if (deleg->d_flags != 0 || deleg->__pad != 0) 1741 + return -EINVAL; 1742 + deleg->d_type = __fcntl_getlease(filp, FL_DELEG); 1743 + return 0; 1732 1744 } 1733 1745 1734 1746 /** ··· 2053 2039 } 2054 2040 EXPORT_SYMBOL_GPL(vfs_setlease); 2055 2041 2056 - static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg) 2042 + static int do_fcntl_add_lease(unsigned int fd, struct file *filp, unsigned int flavor, int arg) 2057 2043 { 2058 2044 struct file_lease *fl; 2059 2045 struct fasync_struct *new; 2060 2046 int error; 2061 2047 2062 - fl = lease_alloc(filp, FL_LEASE, arg); 2048 + fl = lease_alloc(filp, flavor, arg); 2063 2049 if (IS_ERR(fl)) 2064 2050 return PTR_ERR(fl); 2065 2051 ··· 2095 2081 2096 2082 if (arg == F_UNLCK) 2097 2083 return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp); 2098 - return do_fcntl_add_lease(fd, filp, arg); 2084 + return do_fcntl_add_lease(fd, filp, FL_LEASE, arg); 2085 + } 2086 + 2087 + /** 2088 + * fcntl_setdeleg - sets a delegation on an open file 2089 + * @fd: open file descriptor 2090 + * @filp: file pointer 2091 + * @deleg: delegation request from userland 2092 + * 2093 + * Call this fcntl to establish a delegation on the file. 2094 + * Note that you also need to call %F_SETSIG to 2095 + * receive a signal when the lease is broken. 2096 + */ 2097 + int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg) 2098 + { 2099 + /* For now, no flags are supported */ 2100 + if (deleg->d_flags != 0 || deleg->__pad != 0) 2101 + return -EINVAL; 2102 + 2103 + if (deleg->d_type == F_UNLCK) 2104 + return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp); 2105 + return do_fcntl_add_lease(fd, filp, FL_DELEG, deleg->d_type); 2099 2106 } 2100 2107 2101 2108 /**
+12
include/linux/filelock.h
··· 159 159 160 160 int fcntl_setlease(unsigned int fd, struct file *filp, int arg); 161 161 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); 162 164 163 165 static inline bool lock_is_unlock(struct file_lock *fl) 164 166 { ··· 278 276 static inline int fcntl_getlease(struct file *filp) 279 277 { 280 278 return F_UNLCK; 279 + } 280 + 281 + static inline int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *deleg) 282 + { 283 + return -EINVAL; 284 + } 285 + 286 + static inline int fcntl_getdeleg(struct file *filp, struct delegation *deleg) 287 + { 288 + return -EINVAL; 281 289 } 282 290 283 291 static inline bool lock_is_unlock(struct file_lock *fl)
+11
include/uapi/linux/fcntl.h
··· 79 79 */ 80 80 #define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET 81 81 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 + 82 93 /* 83 94 * Types of directory notifications that may be requested. 84 95 */