filelock: add struct delegated_inode

The current API requires a pointer to an inode pointer. It's easy for
callers to get this wrong. Add a new delegated_inode structure and use
that to pass back any inode that needs to be waited on.

Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-3-52f3feebb2f2@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Jeff Layton 2025-11-11 09:12:44 -05:00 committed by Christian Brauner
parent 4be9f3cc58
commit 6976ed2dd0
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
9 changed files with 60 additions and 41 deletions

View File

@ -415,7 +415,7 @@ EXPORT_SYMBOL(may_setattr);
* performed on the raw inode simply pass @nop_mnt_idmap. * performed on the raw inode simply pass @nop_mnt_idmap.
*/ */
int notify_change(struct mnt_idmap *idmap, struct dentry *dentry, int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr, struct inode **delegated_inode) struct iattr *attr, struct delegated_inode *delegated_inode)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode; umode_t mode = inode->i_mode;

View File

@ -4648,7 +4648,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* raw inode simply pass @nop_mnt_idmap. * raw inode simply pass @nop_mnt_idmap.
*/ */
int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir, int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, struct inode **delegated_inode) struct dentry *dentry, struct delegated_inode *delegated_inode)
{ {
struct inode *target = dentry->d_inode; struct inode *target = dentry->d_inode;
int error = may_delete(idmap, dir, dentry, 0); int error = may_delete(idmap, dir, dentry, 0);
@ -4706,7 +4706,7 @@ int do_unlinkat(int dfd, struct filename *name)
struct qstr last; struct qstr last;
int type; int type;
struct inode *inode = NULL; struct inode *inode = NULL;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0;
retry: retry:
error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
@ -4743,7 +4743,7 @@ int do_unlinkat(int dfd, struct filename *name)
if (inode) if (inode)
iput(inode); /* truncate the inode here */ iput(inode); /* truncate the inode here */
inode = NULL; inode = NULL;
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
@ -4892,7 +4892,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
*/ */
int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap, int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
struct inode *dir, struct dentry *new_dentry, struct inode *dir, struct dentry *new_dentry,
struct inode **delegated_inode) struct delegated_inode *delegated_inode)
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links; unsigned max_links = dir->i_sb->s_max_links;
@ -4968,7 +4968,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
struct mnt_idmap *idmap; struct mnt_idmap *idmap;
struct dentry *new_dentry; struct dentry *new_dentry;
struct path old_path, new_path; struct path old_path, new_path;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
int how = 0; int how = 0;
int error; int error;
@ -5012,7 +5012,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
new_dentry, &delegated_inode); new_dentry, &delegated_inode);
out_dput: out_dput:
end_creating_path(&new_path, new_dentry); end_creating_path(&new_path, new_dentry);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) { if (!error) {
path_put(&old_path); path_put(&old_path);
@ -5098,7 +5098,7 @@ int vfs_rename(struct renamedata *rd)
struct inode *new_dir = d_inode(rd->new_parent); struct inode *new_dir = d_inode(rd->new_parent);
struct dentry *old_dentry = rd->old_dentry; struct dentry *old_dentry = rd->old_dentry;
struct dentry *new_dentry = rd->new_dentry; struct dentry *new_dentry = rd->new_dentry;
struct inode **delegated_inode = rd->delegated_inode; struct delegated_inode *delegated_inode = rd->delegated_inode;
unsigned int flags = rd->flags; unsigned int flags = rd->flags;
bool is_dir = d_is_dir(old_dentry); bool is_dir = d_is_dir(old_dentry);
struct inode *source = old_dentry->d_inode; struct inode *source = old_dentry->d_inode;
@ -5261,7 +5261,7 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
struct path old_path, new_path; struct path old_path, new_path;
struct qstr old_last, new_last; struct qstr old_last, new_last;
int old_type, new_type; int old_type, new_type;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
unsigned int lookup_flags = 0, target_flags = unsigned int lookup_flags = 0, target_flags =
LOOKUP_RENAME_TARGET | LOOKUP_CREATE; LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
bool should_retry = false; bool should_retry = false;
@ -5369,7 +5369,7 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
exit3: exit3:
unlock_rename(new_path.dentry, old_path.dentry); unlock_rename(new_path.dentry, old_path.dentry);
exit_lock_rename: exit_lock_rename:
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;

View File

@ -631,7 +631,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
int chmod_common(const struct path *path, umode_t mode) int chmod_common(const struct path *path, umode_t mode)
{ {
struct inode *inode = path->dentry->d_inode; struct inode *inode = path->dentry->d_inode;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
struct iattr newattrs; struct iattr newattrs;
int error; int error;
@ -651,7 +651,7 @@ int chmod_common(const struct path *path, umode_t mode)
&newattrs, &delegated_inode); &newattrs, &delegated_inode);
out_unlock: out_unlock:
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
@ -756,7 +756,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
struct mnt_idmap *idmap; struct mnt_idmap *idmap;
struct user_namespace *fs_userns; struct user_namespace *fs_userns;
struct inode *inode = path->dentry->d_inode; struct inode *inode = path->dentry->d_inode;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
int error; int error;
struct iattr newattrs; struct iattr newattrs;
kuid_t uid; kuid_t uid;
@ -791,7 +791,7 @@ int chown_common(const struct path *path, uid_t user, gid_t group)
error = notify_change(idmap, path->dentry, &newattrs, error = notify_change(idmap, path->dentry, &newattrs,
&delegated_inode); &delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;

View File

@ -1091,7 +1091,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
int acl_type; int acl_type;
int error; int error;
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
acl_type = posix_acl_type(acl_name); acl_type = posix_acl_type(acl_name);
if (acl_type < 0) if (acl_type < 0)
@ -1141,7 +1141,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
out_inode_unlock: out_inode_unlock:
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
@ -1212,7 +1212,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
int acl_type; int acl_type;
int error; int error;
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
acl_type = posix_acl_type(acl_name); acl_type = posix_acl_type(acl_name);
if (acl_type < 0) if (acl_type < 0)
@ -1249,7 +1249,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
out_inode_unlock: out_inode_unlock:
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;

View File

@ -22,7 +22,7 @@ int vfs_utimes(const struct path *path, struct timespec64 *times)
int error; int error;
struct iattr newattrs; struct iattr newattrs;
struct inode *inode = path->dentry->d_inode; struct inode *inode = path->dentry->d_inode;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
if (times) { if (times) {
if (!nsec_valid(times[0].tv_nsec) || if (!nsec_valid(times[0].tv_nsec) ||
@ -66,7 +66,7 @@ int vfs_utimes(const struct path *path, struct timespec64 *times)
error = notify_change(mnt_idmap(path->mnt), path->dentry, &newattrs, error = notify_change(mnt_idmap(path->mnt), path->dentry, &newattrs,
&delegated_inode); &delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;

View File

@ -274,7 +274,7 @@ int __vfs_setxattr_noperm(struct mnt_idmap *idmap,
int int
__vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry, __vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name, const void *value, size_t size, const char *name, const void *value, size_t size,
int flags, struct inode **delegated_inode) int flags, struct delegated_inode *delegated_inode)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error; int error;
@ -305,7 +305,7 @@ vfs_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name, const void *value, size_t size, int flags) const char *name, const void *value, size_t size, int flags)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
const void *orig_value = value; const void *orig_value = value;
int error; int error;
@ -322,7 +322,7 @@ vfs_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
flags, &delegated_inode); flags, &delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
@ -533,7 +533,7 @@ EXPORT_SYMBOL(__vfs_removexattr);
int int
__vfs_removexattr_locked(struct mnt_idmap *idmap, __vfs_removexattr_locked(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, struct dentry *dentry, const char *name,
struct inode **delegated_inode) struct delegated_inode *delegated_inode)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int error; int error;
@ -567,7 +567,7 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
const char *name) const char *name)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct inode *delegated_inode = NULL; struct delegated_inode delegated_inode = { };
int error; int error;
retry_deleg: retry_deleg:
@ -576,7 +576,7 @@ vfs_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
name, &delegated_inode); name, &delegated_inode);
inode_unlock(inode); inode_unlock(inode);
if (delegated_inode) { if (is_delegated(&delegated_inode)) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;

View File

@ -486,25 +486,35 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0; return 0;
} }
static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode) struct delegated_inode {
struct inode *di_inode;
};
static inline bool is_delegated(struct delegated_inode *di)
{
return di->di_inode;
}
static inline int try_break_deleg(struct inode *inode,
struct delegated_inode *di)
{ {
int ret; int ret;
ret = break_deleg(inode, LEASE_BREAK_NONBLOCK); ret = break_deleg(inode, LEASE_BREAK_NONBLOCK);
if (ret == -EWOULDBLOCK && delegated_inode) { if (ret == -EWOULDBLOCK && di) {
*delegated_inode = inode; di->di_inode = inode;
ihold(inode); ihold(inode);
} }
return ret; return ret;
} }
static inline int break_deleg_wait(struct inode **delegated_inode) static inline int break_deleg_wait(struct delegated_inode *di)
{ {
int ret; int ret;
ret = break_deleg(*delegated_inode, 0); ret = break_deleg(di->di_inode, 0);
iput(*delegated_inode); iput(di->di_inode);
*delegated_inode = NULL; di->di_inode = NULL;
return ret; return ret;
} }
@ -523,6 +533,13 @@ static inline int break_layout(struct inode *inode, bool wait)
} }
#else /* !CONFIG_FILE_LOCKING */ #else /* !CONFIG_FILE_LOCKING */
struct delegated_inode { };
static inline bool is_delegated(struct delegated_inode *di)
{
return false;
}
static inline int break_lease(struct inode *inode, bool wait) static inline int break_lease(struct inode *inode, bool wait)
{ {
return 0; return 0;
@ -533,12 +550,13 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0; return 0;
} }
static inline int try_break_deleg(struct inode *inode, struct inode **delegated_inode) static inline int try_break_deleg(struct inode *inode,
struct delegated_inode *delegated_inode)
{ {
return 0; return 0;
} }
static inline int break_deleg_wait(struct inode **delegated_inode) static inline int break_deleg_wait(struct delegated_inode *delegated_inode)
{ {
BUG(); BUG();
return 0; return 0;

View File

@ -80,6 +80,7 @@ struct fs_context;
struct fs_parameter_spec; struct fs_parameter_spec;
struct file_kattr; struct file_kattr;
struct iomap_ops; struct iomap_ops;
struct delegated_inode;
extern void __init inode_init(void); extern void __init inode_init(void);
extern void __init inode_init_early(void); extern void __init inode_init_early(void);
@ -2119,10 +2120,10 @@ int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
int vfs_symlink(struct mnt_idmap *, struct inode *, int vfs_symlink(struct mnt_idmap *, struct inode *,
struct dentry *, const char *); struct dentry *, const char *);
int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *, int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *,
struct dentry *, struct inode **); struct dentry *, struct delegated_inode *);
int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *); int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *);
int vfs_unlink(struct mnt_idmap *, struct inode *, struct dentry *, int vfs_unlink(struct mnt_idmap *, struct inode *, struct dentry *,
struct inode **); struct delegated_inode *);
/** /**
* struct renamedata - contains all information required for renaming * struct renamedata - contains all information required for renaming
@ -2140,7 +2141,7 @@ struct renamedata {
struct dentry *old_dentry; struct dentry *old_dentry;
struct dentry *new_parent; struct dentry *new_parent;
struct dentry *new_dentry; struct dentry *new_dentry;
struct inode **delegated_inode; struct delegated_inode *delegated_inode;
unsigned int flags; unsigned int flags;
} __randomize_layout; } __randomize_layout;
@ -3071,7 +3072,7 @@ static inline int bmap(struct inode *inode, sector_t *block)
#endif #endif
int notify_change(struct mnt_idmap *, struct dentry *, int notify_change(struct mnt_idmap *, struct dentry *,
struct iattr *, struct inode **); struct iattr *, struct delegated_inode *);
int inode_permission(struct mnt_idmap *, struct inode *, int); int inode_permission(struct mnt_idmap *, struct inode *, int);
int generic_permission(struct mnt_idmap *, struct inode *, int); int generic_permission(struct mnt_idmap *, struct inode *, int);
static inline int file_permission(struct file *file, int mask) static inline int file_permission(struct file *file, int mask)

View File

@ -85,12 +85,12 @@ int __vfs_setxattr_noperm(struct mnt_idmap *, struct dentry *,
const char *, const void *, size_t, int); const char *, const void *, size_t, int);
int __vfs_setxattr_locked(struct mnt_idmap *, struct dentry *, int __vfs_setxattr_locked(struct mnt_idmap *, struct dentry *,
const char *, const void *, size_t, int, const char *, const void *, size_t, int,
struct inode **); struct delegated_inode *);
int vfs_setxattr(struct mnt_idmap *, struct dentry *, const char *, int vfs_setxattr(struct mnt_idmap *, struct dentry *, const char *,
const void *, size_t, int); const void *, size_t, int);
int __vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *); int __vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *);
int __vfs_removexattr_locked(struct mnt_idmap *, struct dentry *, int __vfs_removexattr_locked(struct mnt_idmap *, struct dentry *,
const char *, struct inode **); const char *, struct delegated_inode *);
int vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *); int vfs_removexattr(struct mnt_idmap *, struct dentry *, const char *);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);