1
0
mirror of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2026-01-11 17:10:13 +00:00
Linus Torvalds a8058f8442 vfs-6.19-rc1.directory.locking
-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaSmOZwAKCRCRxhvAZXjc
 op9tAQCJ//STOkvYHfqgsdRD+cW9MRg/gPzfVZgnV1FTyf8sMgEA0IsY5zCZB9eh
 9FdD0E57P8PlWRwWZ+LktnWBzRAUqwI=
 =MOVR
 -----END PGP SIGNATURE-----

Merge tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull directory locking updates from Christian Brauner:
 "This contains the work to add centralized APIs for directory locking
  operations.

  This series is part of a larger effort to change directory operation
  locking to allow multiple concurrent operations in a directory. The
  ultimate goal is to lock the target dentry(s) rather than the whole
  parent directory.

  To help with changing the locking protocol, this series centralizes
  locking and lookup in new helper functions. The helpers establish a
  pattern where it is the dentry that is being locked and unlocked
  (currently the lock is held on dentry->d_parent->d_inode, but that can
  change in the future).

  This also changes vfs_mkdir() to unlock the parent on failure, as well
  as dput()ing the dentry. This allows end_creating() to only require
  the target dentry (which may be IS_ERR() after vfs_mkdir()), not the
  parent"

* tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  nfsd: fix end_creating() conversion
  VFS: introduce end_creating_keep()
  VFS: change vfs_mkdir() to unlock on failure.
  ecryptfs: use new start_creating/start_removing APIs
  Add start_renaming_two_dentries()
  VFS/ovl/smb: introduce start_renaming_dentry()
  VFS/nfsd/ovl: introduce start_renaming() and end_renaming()
  VFS: add start_creating_killable() and start_removing_killable()
  VFS: introduce start_removing_dentry()
  smb/server: use end_removing_noperm for for target of smb2_create_link()
  VFS: introduce start_creating_noperm() and start_removing_noperm()
  VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing()
  VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()
  VFS: tidy up do_unlinkat()
  VFS: introduce start_dirop() and end_dirop()
  debugfs: rename end_creating() to debugfs_end_creating()
2025-12-01 16:13:46 -08:00

230 lines
8.8 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_NAMEI_H
#define _LINUX_NAMEI_H
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/path.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <linux/fs_struct.h>
enum { MAX_NESTED_LINKS = 8 };
#define MAXSYMLINKS 40
/*
* Type of the last component on LOOKUP_PARENT
*/
enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
/* pathwalk mode */
#define LOOKUP_FOLLOW BIT(0) /* follow links at the end */
#define LOOKUP_DIRECTORY BIT(1) /* require a directory */
#define LOOKUP_AUTOMOUNT BIT(2) /* force terminal automount */
#define LOOKUP_EMPTY BIT(3) /* accept empty path [user_... only] */
#define LOOKUP_LINKAT_EMPTY BIT(4) /* Linkat request with empty path. */
#define LOOKUP_DOWN BIT(5) /* follow mounts in the starting point */
#define LOOKUP_MOUNTPOINT BIT(6) /* follow mounts in the end */
#define LOOKUP_REVAL BIT(7) /* tell ->d_revalidate() to trust no cache */
#define LOOKUP_RCU BIT(8) /* RCU pathwalk mode; semi-internal */
#define LOOKUP_CACHED BIT(9) /* Only do cached lookup */
#define LOOKUP_PARENT BIT(10) /* Looking up final parent in path */
/* 5 spare bits for pathwalk */
/* These tell filesystem methods that we are dealing with the final component... */
#define LOOKUP_OPEN BIT(16) /* ... in open */
#define LOOKUP_CREATE BIT(17) /* ... in object creation */
#define LOOKUP_EXCL BIT(18) /* ... in target must not exist */
#define LOOKUP_RENAME_TARGET BIT(19) /* ... in destination of rename() */
/* 4 spare bits for intent */
/* Scoping flags for lookup. */
#define LOOKUP_NO_SYMLINKS BIT(24) /* No symlink crossing. */
#define LOOKUP_NO_MAGICLINKS BIT(25) /* No nd_jump_link() crossing. */
#define LOOKUP_NO_XDEV BIT(26) /* No mountpoint crossing. */
#define LOOKUP_BENEATH BIT(27) /* No escaping from starting point. */
#define LOOKUP_IN_ROOT BIT(28) /* Treat dirfd as fs root. */
/* LOOKUP_* flags which do scope-related checks based on the dirfd. */
#define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT)
/* 3 spare bits for scoping */
extern int path_pts(struct path *path);
extern int user_path_at(int, const char __user *, unsigned, struct path *);
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
struct dentry *base,
unsigned int flags);
extern int kern_path(const char *, unsigned, struct path *);
struct dentry *kern_path_parent(const char *name, struct path *parent);
extern struct dentry *start_creating_path(int, const char *, struct path *, unsigned int);
extern struct dentry *start_creating_user_path(int, const char __user *, struct path *, unsigned int);
extern void end_creating_path(const struct path *, struct dentry *);
extern struct dentry *start_removing_path(const char *, struct path *);
extern struct dentry *start_removing_user_path_at(int , const char __user *, struct path *);
static inline void end_removing_path(const struct path *path , struct dentry *dentry)
{
end_creating_path(path, dentry);
}
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
struct path *parent, struct qstr *last, int *type,
const struct path *root);
int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *,
unsigned int, struct path *);
extern struct dentry *try_lookup_noperm(struct qstr *, struct dentry *);
extern struct dentry *lookup_noperm(struct qstr *, struct dentry *);
extern struct dentry *lookup_noperm_unlocked(struct qstr *, struct dentry *);
extern struct dentry *lookup_noperm_positive_unlocked(struct qstr *, struct dentry *);
struct dentry *lookup_one(struct mnt_idmap *, struct qstr *, struct dentry *);
struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
struct qstr *name, struct dentry *base);
struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
struct qstr *name,
struct dentry *base);
struct dentry *lookup_one_positive_killable(struct mnt_idmap *idmap,
struct qstr *name,
struct dentry *base);
struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
struct qstr *name);
struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
struct qstr *name);
struct dentry *start_creating_killable(struct mnt_idmap *idmap,
struct dentry *parent,
struct qstr *name);
struct dentry *start_removing_killable(struct mnt_idmap *idmap,
struct dentry *parent,
struct qstr *name);
struct dentry *start_creating_noperm(struct dentry *parent, struct qstr *name);
struct dentry *start_removing_noperm(struct dentry *parent, struct qstr *name);
struct dentry *start_creating_dentry(struct dentry *parent,
struct dentry *child);
struct dentry *start_removing_dentry(struct dentry *parent,
struct dentry *child);
/* end_creating - finish action started with start_creating
* @child: dentry returned by start_creating() or vfs_mkdir()
*
* Unlock and release the child. This can be called after
* start_creating() whether that function succeeded or not,
* but it is not needed on failure.
*
* If vfs_mkdir() was called then the value returned from that function
* should be given for @child rather than the original dentry, as vfs_mkdir()
* may have provided a new dentry.
*
*
* If vfs_mkdir() was not called, then @child will be a valid dentry and
* @parent will be ignored.
*/
static inline void end_creating(struct dentry *child)
{
end_dirop(child);
}
/* end_creating_keep - finish action started with start_creating() and return result
* @child: dentry returned by start_creating() or vfs_mkdir()
*
* Unlock and return the child. This can be called after
* start_creating() whether that function succeeded or not,
* but it is not needed on failure.
*
* If vfs_mkdir() was called then the value returned from that function
* should be given for @child rather than the original dentry, as vfs_mkdir()
* may have provided a new dentry.
*
* Returns: @child, which may be a dentry or an error.
*
*/
static inline struct dentry *end_creating_keep(struct dentry *child)
{
if (!IS_ERR(child))
dget(child);
end_dirop(child);
return child;
}
/**
* end_removing - finish action started with start_removing
* @child: dentry returned by start_removing()
* @parent: dentry given to start_removing()
*
* Unlock and release the child.
*
* This is identical to end_dirop(). It can be passed the result of
* start_removing() whether that was successful or not, but it not needed
* if start_removing() failed.
*/
static inline void end_removing(struct dentry *child)
{
end_dirop(child);
}
extern int follow_down_one(struct path *);
extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
extern void unlock_rename(struct dentry *, struct dentry *);
int start_renaming(struct renamedata *rd, int lookup_flags,
struct qstr *old_last, struct qstr *new_last);
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
struct dentry *old_dentry, struct qstr *new_last);
int start_renaming_two_dentries(struct renamedata *rd,
struct dentry *old_dentry, struct dentry *new_dentry);
void end_renaming(struct renamedata *rd);
/**
* mode_strip_umask - handle vfs umask stripping
* @dir: parent directory of the new inode
* @mode: mode of the new inode to be created in @dir
*
* In most filesystems, umask stripping depends on whether or not the
* filesystem supports POSIX ACLs. If the filesystem doesn't support it umask
* stripping is done directly in here. If the filesystem does support POSIX
* ACLs umask stripping is deferred until the filesystem calls
* posix_acl_create().
*
* Some filesystems (like NFSv4) also want to avoid umask stripping by the
* VFS, but don't support POSIX ACLs. Those filesystems can set SB_I_NOUMASK
* to get this effect without declaring that they support POSIX ACLs.
*
* Returns: mode
*/
static inline umode_t __must_check mode_strip_umask(const struct inode *dir, umode_t mode)
{
if (!IS_POSIXACL(dir) && !(dir->i_sb->s_iflags & SB_I_NOUMASK))
mode &= ~current_umask();
return mode;
}
extern int __must_check nd_jump_link(const struct path *path);
static inline void nd_terminate_link(void *name, size_t len, size_t maxlen)
{
((char *) name)[min(len, maxlen)] = '\0';
}
/**
* retry_estale - determine whether the caller should retry an operation
* @error: the error that would currently be returned
* @flags: flags being used for next lookup attempt
*
* Check to see if the error code was -ESTALE, and then determine whether
* to retry the call based on whether "flags" already has LOOKUP_REVAL set.
*
* Returns true if the caller should try the operation again.
*/
static inline bool
retry_estale(const long error, const unsigned int flags)
{
return unlikely(error == -ESTALE && !(flags & LOOKUP_REVAL));
}
#endif /* _LINUX_NAMEI_H */