mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
![Al Viro](/assets/img/avatar_default.png)
* RCU-delayed freeing of vfsmounts * vfsmount_lock replaced with a seqlock (mount_lock) * sequence number from mount_lock is stored in nameidata->m_seq and used when we exit RCU mode * new vfsmount flag - MNT_SYNC_UMOUNT. Set by umount_tree() when its caller knows that vfsmount will have no surviving references. * synchronize_rcu() done between unlocking namespace_sem in namespace_unlock() and doing pending mntput(). * new helper: legitimize_mnt(mnt, seq). Checks the mount_lock sequence number against seq, then grabs reference to mnt. Then it rechecks mount_lock again to close the race and either returns success or drops the reference it has acquired. The subtle point is that in case of MNT_SYNC_UMOUNT we can simply decrement the refcount and sod off - aforementioned synchronize_rcu() makes sure that final mntput() won't come until we leave RCU mode. We need that, since we don't want to end up with some lazy pathwalk racing with umount() and stealing the final mntput() from it - caller of umount() may expect it to return only once the fs is shut down and we don't want to break that. In other cases (i.e. with MNT_SYNC_UMOUNT absent) we have to do full-blown mntput() in case of mount_lock sequence number mismatch happening just as we'd grabbed the reference, but in those cases we won't be stealing the final mntput() from anything that would care. * mntput_no_expire() doesn't lock anything on the fast path now. Incidentally, SMP and UP cases are handled the same way - no ifdefs there. * normal pathname resolution does *not* do any writes to mount_lock. It does, of course, bump the refcounts of vfsmount and dentry in the very end, but that's it. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
117 lines
3.4 KiB
C
117 lines
3.4 KiB
C
#ifndef _LINUX_NAMEI_H
|
|
#define _LINUX_NAMEI_H
|
|
|
|
#include <linux/dcache.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/linkage.h>
|
|
#include <linux/path.h>
|
|
|
|
struct vfsmount;
|
|
|
|
enum { MAX_NESTED_LINKS = 8 };
|
|
|
|
struct nameidata {
|
|
struct path path;
|
|
struct qstr last;
|
|
struct path root;
|
|
struct inode *inode; /* path.dentry.d_inode */
|
|
unsigned int flags;
|
|
unsigned seq, m_seq;
|
|
int last_type;
|
|
unsigned depth;
|
|
char *saved_names[MAX_NESTED_LINKS + 1];
|
|
};
|
|
|
|
/*
|
|
* Type of the last component on LOOKUP_PARENT
|
|
*/
|
|
enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
|
|
|
/*
|
|
* The bitmask for a lookup event:
|
|
* - follow links at the end
|
|
* - require a directory
|
|
* - ending slashes ok even for nonexistent files
|
|
* - internal "there are more path components" flag
|
|
* - dentry cache is untrusted; force a real lookup
|
|
* - suppress terminal automount
|
|
*/
|
|
#define LOOKUP_FOLLOW 0x0001
|
|
#define LOOKUP_DIRECTORY 0x0002
|
|
#define LOOKUP_AUTOMOUNT 0x0004
|
|
|
|
#define LOOKUP_PARENT 0x0010
|
|
#define LOOKUP_REVAL 0x0020
|
|
#define LOOKUP_RCU 0x0040
|
|
|
|
/*
|
|
* Intent data
|
|
*/
|
|
#define LOOKUP_OPEN 0x0100
|
|
#define LOOKUP_CREATE 0x0200
|
|
#define LOOKUP_EXCL 0x0400
|
|
#define LOOKUP_RENAME_TARGET 0x0800
|
|
|
|
#define LOOKUP_JUMPED 0x1000
|
|
#define LOOKUP_ROOT 0x2000
|
|
#define LOOKUP_EMPTY 0x4000
|
|
|
|
extern int user_path_at(int, const char __user *, unsigned, struct path *);
|
|
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
|
|
|
|
#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
|
|
#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
|
|
#define user_path_dir(name, path) \
|
|
user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path)
|
|
|
|
extern int kern_path(const char *, unsigned, struct path *);
|
|
|
|
extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
|
|
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
|
|
extern void done_path_create(struct path *, struct dentry *);
|
|
extern struct dentry *kern_path_locked(const char *, struct path *);
|
|
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
|
|
|
|
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
|
|
|
extern int follow_down_one(struct path *);
|
|
extern int follow_down(struct path *);
|
|
extern int follow_up(struct path *);
|
|
|
|
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
|
|
extern void unlock_rename(struct dentry *, struct dentry *);
|
|
|
|
extern void nd_jump_link(struct nameidata *nd, struct path *path);
|
|
|
|
static inline void nd_set_link(struct nameidata *nd, char *path)
|
|
{
|
|
nd->saved_names[nd->depth] = path;
|
|
}
|
|
|
|
static inline char *nd_get_link(struct nameidata *nd)
|
|
{
|
|
return nd->saved_names[nd->depth];
|
|
}
|
|
|
|
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 error == -ESTALE && !(flags & LOOKUP_REVAL);
|
|
}
|
|
|
|
#endif /* _LINUX_NAMEI_H */
|