Commit 9763f7a4 authored by Al Viro's avatar Al Viro

Merge branch 'work.autofs' into for-linus

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parents 3c55d6bc 1c4344a5
......@@ -20,7 +20,7 @@ prototypes:
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
struct vfsmount *(*d_automount)(struct path *path);
int (*d_manage)(struct dentry *, bool);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
......
......@@ -948,7 +948,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
};
......
......@@ -145,7 +145,7 @@ void autofs4_free_ino(struct autofs_info *);
/* Expiration */
int is_autofs4_dentry(struct dentry *);
int autofs4_expire_wait(struct dentry *dentry, int rcu_walk);
int autofs4_expire_wait(const struct path *path, int rcu_walk);
int autofs4_expire_run(struct super_block *, struct vfsmount *,
struct autofs_sb_info *,
struct autofs_packet_expire __user *);
......@@ -217,7 +217,8 @@ static inline int autofs_prepare_pipe(struct file *pipe)
/* Queue management functions */
int autofs4_wait(struct autofs_sb_info *, struct dentry *, enum autofs_notify);
int autofs4_wait(struct autofs_sb_info *,
const struct path *, enum autofs_notify);
int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int);
void autofs4_catatonic_mode(struct autofs_sb_info *);
......
......@@ -468,7 +468,7 @@ static int autofs_dev_ioctl_requester(struct file *fp,
ino = autofs4_dentry_ino(path.dentry);
if (ino) {
err = 0;
autofs4_expire_wait(path.dentry, 0);
autofs4_expire_wait(&path, 0);
spin_lock(&sbi->fs_lock);
param->requester.uid =
from_kuid_munged(current_user_ns(), ino->uid);
......@@ -575,7 +575,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
devid = new_encode_dev(dev);
err = have_submounts(path.dentry);
err = path_has_submounts(&path);
if (follow_down_one(&path))
magic = path.dentry->d_sb->s_magic;
......
......@@ -310,26 +310,29 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
now = jiffies;
timeout = sbi->exp_timeout;
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(root);
/* No point expiring a pending mount */
if (ino->flags & AUTOFS_INF_PENDING)
goto out;
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(root);
/* No point expiring a pending mount */
if (ino->flags & AUTOFS_INF_PENDING) {
spin_unlock(&sbi->fs_lock);
goto out;
}
ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu();
spin_lock(&sbi->fs_lock);
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
spin_lock(&sbi->fs_lock);
ino->flags |= AUTOFS_INF_EXPIRING;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
spin_lock(&sbi->fs_lock);
ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
}
out:
spin_unlock(&sbi->fs_lock);
dput(root);
return NULL;
......@@ -495,8 +498,9 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
return expired;
}
int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
int autofs4_expire_wait(const struct path *path, int rcu_walk)
{
struct dentry *dentry = path->dentry;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
int status;
......@@ -525,7 +529,7 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
status = autofs4_wait(sbi, dentry, NFY_NONE);
status = autofs4_wait(sbi, path, NFY_NONE);
wait_for_completion(&ino->expire_complete);
pr_debug("expire done status=%d\n", status);
......@@ -592,11 +596,12 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
if (dentry) {
struct autofs_info *ino = autofs4_dentry_ino(dentry);
const struct path path = { .mnt = mnt, .dentry = dentry };
/* This is synchronous because it makes the daemon a
* little easier
*/
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
ret = autofs4_wait(sbi, &path, NFY_EXPIRE);
spin_lock(&sbi->fs_lock);
/* avoid rapid-fire expire attempts if expiry fails */
......
......@@ -32,7 +32,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file);
static struct dentry *autofs4_lookup(struct inode *,
struct dentry *, unsigned int);
static struct vfsmount *autofs4_d_automount(struct path *);
static int autofs4_d_manage(struct dentry *, bool);
static int autofs4_d_manage(const struct path *, bool);
static void autofs4_dentry_release(struct dentry *);
const struct file_operations autofs4_root_operations = {
......@@ -123,7 +123,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
* it.
*/
spin_lock(&sbi->lookup_lock);
if (!d_mountpoint(dentry) && simple_empty(dentry)) {
if (!path_is_mountpoint(&file->f_path) && simple_empty(dentry)) {
spin_unlock(&sbi->lookup_lock);
return -ENOENT;
}
......@@ -269,39 +269,41 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry,
return NULL;
}
static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
static int autofs4_mount_wait(const struct path *path, bool rcu_walk)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_sb_info *sbi = autofs4_sbi(path->dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(path->dentry);
int status = 0;
if (ino->flags & AUTOFS_INF_PENDING) {
if (rcu_walk)
return -ECHILD;
pr_debug("waiting for mount name=%pd\n", dentry);
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
pr_debug("waiting for mount name=%pd\n", path->dentry);
status = autofs4_wait(sbi, path, NFY_MOUNT);
pr_debug("mount wait done status=%d\n", status);
}
ino->last_used = jiffies;
return status;
}
static int do_expire_wait(struct dentry *dentry, bool rcu_walk)
static int do_expire_wait(const struct path *path, bool rcu_walk)
{
struct dentry *dentry = path->dentry;
struct dentry *expiring;
expiring = autofs4_lookup_expiring(dentry, rcu_walk);
if (IS_ERR(expiring))
return PTR_ERR(expiring);
if (!expiring)
return autofs4_expire_wait(dentry, rcu_walk);
return autofs4_expire_wait(path, rcu_walk);
else {
const struct path this = { .mnt = path->mnt, .dentry = expiring };
/*
* If we are racing with expire the request might not
* be quite complete, but the directory has been removed
* so it must have been successful, just wait for it.
*/
autofs4_expire_wait(expiring, 0);
autofs4_expire_wait(&this, 0);
autofs4_del_expiring(expiring);
dput(expiring);
}
......@@ -354,7 +356,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
* and the directory was removed, so just go ahead and try
* the mount.
*/
status = do_expire_wait(dentry, 0);
status = do_expire_wait(path, 0);
if (status && status != -EAGAIN)
return NULL;
......@@ -362,7 +364,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
spin_lock(&sbi->fs_lock);
if (ino->flags & AUTOFS_INF_PENDING) {
spin_unlock(&sbi->fs_lock);
status = autofs4_mount_wait(dentry, 0);
status = autofs4_mount_wait(path, 0);
if (status)
return ERR_PTR(status);
goto done;
......@@ -370,28 +372,28 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
/*
* If the dentry is a symlink it's equivalent to a directory
* having d_mountpoint() true, so there's no need to call back
* to the daemon.
* having path_is_mountpoint() true, so there's no need to call
* back to the daemon.
*/
if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
spin_unlock(&sbi->fs_lock);
goto done;
}
if (!d_mountpoint(dentry)) {
if (!path_is_mountpoint(path)) {
/*
* It's possible that user space hasn't removed directories
* after umounting a rootless multi-mount, although it
* should. For v5 have_submounts() is sufficient to handle
* this because the leaves of the directory tree under the
* mount never trigger mounts themselves (they have an autofs
* trigger mount mounted on them). But v4 pseudo direct mounts
* do need the leaves to trigger mounts. In this case we
* have no choice but to use the list_empty() check and
* should. For v5 path_has_submounts() is sufficient to
* handle this because the leaves of the directory tree under
* the mount never trigger mounts themselves (they have an
* autofs trigger mount mounted on them). But v4 pseudo direct
* mounts do need the leaves to trigger mounts. In this case
* we have no choice but to use the list_empty() check and
* require user space behave.
*/
if (sbi->version > 4) {
if (have_submounts(dentry)) {
if (path_has_submounts(path)) {
spin_unlock(&sbi->fs_lock);
goto done;
}
......@@ -403,7 +405,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
}
ino->flags |= AUTOFS_INF_PENDING;
spin_unlock(&sbi->fs_lock);
status = autofs4_mount_wait(dentry, 0);
status = autofs4_mount_wait(path, 0);
spin_lock(&sbi->fs_lock);
ino->flags &= ~AUTOFS_INF_PENDING;
if (status) {
......@@ -421,8 +423,9 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
return NULL;
}
static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
static int autofs4_d_manage(const struct path *path, bool rcu_walk)
{
struct dentry *dentry = path->dentry;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
int status;
......@@ -431,20 +434,20 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
/* The daemon never waits. */
if (autofs4_oz_mode(sbi)) {
if (!d_mountpoint(dentry))
if (!path_is_mountpoint(path))
return -EISDIR;
return 0;
}
/* Wait for pending expires */
if (do_expire_wait(dentry, rcu_walk) == -ECHILD)
if (do_expire_wait(path, rcu_walk) == -ECHILD)
return -ECHILD;
/*
* This dentry may be under construction so wait on mount
* completion.
*/
status = autofs4_mount_wait(dentry, rcu_walk);
status = autofs4_mount_wait(path, rcu_walk);
if (status)
return status;
......@@ -460,7 +463,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
return 0;
if (d_mountpoint(dentry))
if (path_is_mountpoint(path))
return 0;
inode = d_inode_rcu(dentry);
if (inode && S_ISLNK(inode->i_mode))
......@@ -487,7 +490,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
* we can avoid needless calls ->d_automount() and avoid
* an incorrect ELOOP error return.
*/
if ((!d_mountpoint(dentry) && !simple_empty(dentry)) ||
if ((!path_is_mountpoint(path) && !simple_empty(dentry)) ||
(d_really_is_positive(dentry) && d_is_symlink(dentry)))
status = -EISDIR;
}
......
......@@ -250,8 +250,9 @@ autofs4_find_wait(struct autofs_sb_info *sbi, const struct qstr *qstr)
static int validate_request(struct autofs_wait_queue **wait,
struct autofs_sb_info *sbi,
const struct qstr *qstr,
struct dentry *dentry, enum autofs_notify notify)
const struct path *path, enum autofs_notify notify)
{
struct dentry *dentry = path->dentry;
struct autofs_wait_queue *wq;
struct autofs_info *ino;
......@@ -314,6 +315,7 @@ static int validate_request(struct autofs_wait_queue **wait,
*/
if (notify == NFY_MOUNT) {
struct dentry *new = NULL;
struct path this;
int valid = 1;
/*
......@@ -333,7 +335,9 @@ static int validate_request(struct autofs_wait_queue **wait,
dentry = new;
}
}
if (have_submounts(dentry))
this.mnt = path->mnt;
this.dentry = dentry;
if (path_has_submounts(&this))
valid = 0;
if (new)
......@@ -345,8 +349,9 @@ static int validate_request(struct autofs_wait_queue **wait,
}
int autofs4_wait(struct autofs_sb_info *sbi,
struct dentry *dentry, enum autofs_notify notify)
const struct path *path, enum autofs_notify notify)
{
struct dentry *dentry = path->dentry;
struct autofs_wait_queue *wq;
struct qstr qstr;
char *name;
......@@ -405,7 +410,7 @@ int autofs4_wait(struct autofs_sb_info *sbi,
return -EINTR;
}
ret = validate_request(&wq, sbi, &qstr, dentry, notify);
ret = validate_request(&wq, sbi, &qstr, path, notify);
if (ret <= 0) {
if (ret != -EINTR)
mutex_unlock(&sbi->wq_mutex);
......
......@@ -1273,38 +1273,44 @@ static void d_walk(struct dentry *parent, void *data,
goto again;
}
/*
* Search for at least 1 mount point in the dentry's subdirs.
* We descend to the next level whenever the d_subdirs
* list is non-empty and continue searching.
*/
struct check_mount {
struct vfsmount *mnt;
unsigned int mounted;
};
static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
static enum d_walk_ret path_check_mount(void *data, struct dentry *dentry)
{
int *ret = data;
if (d_mountpoint(dentry)) {
*ret = 1;
struct check_mount *info = data;
struct path path = { .mnt = info->mnt, .dentry = dentry };
if (likely(!d_mountpoint(dentry)))
return D_WALK_CONTINUE;
if (__path_is_mountpoint(&path)) {
info->mounted = 1;
return D_WALK_QUIT;
}
return D_WALK_CONTINUE;
}
/**
* have_submounts - check for mounts over a dentry
* @parent: dentry to check.
* path_has_submounts - check for mounts over a dentry in the
* current namespace.
* @parent: path to check.
*
* Return true if the parent or its subdirectories contain
* a mount point
* a mount point in the current namespace.
*/
int have_submounts(struct dentry *parent)
int path_has_submounts(const struct path *parent)
{
int ret = 0;
struct check_mount data = { .mnt = parent->mnt, .mounted = 0 };
d_walk(parent, &ret, check_mount, NULL);
read_seqlock_excl(&mount_lock);
d_walk(parent->dentry, &data, path_check_mount, NULL);
read_sequnlock_excl(&mount_lock);
return ret;
return data.mounted;
}
EXPORT_SYMBOL(have_submounts);
EXPORT_SYMBOL(path_has_submounts);
/*
* Called by mount code to set a mountpoint and check if the mountpoint is
......
......@@ -94,6 +94,12 @@ extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *);
extern int __legitimize_mnt(struct vfsmount *, unsigned);
extern bool legitimize_mnt(struct vfsmount *, unsigned);
static inline bool __path_is_mountpoint(const struct path *path)
{
struct mount *m = __lookup_mnt(path->mnt, path->dentry);
return m && likely(!(m->mnt.mnt_flags & MNT_SYNC_UMOUNT));
}
extern void __detach_mounts(struct dentry *dentry);
static inline void detach_mounts(struct dentry *dentry)
......
......@@ -1200,7 +1200,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage(path->dentry, false);
ret = path->dentry->d_op->d_manage(path, false);
if (ret < 0)
break;
}
......@@ -1263,10 +1263,10 @@ int follow_down_one(struct path *path)
}
EXPORT_SYMBOL(follow_down_one);
static inline int managed_dentry_rcu(struct dentry *dentry)
static inline int managed_dentry_rcu(const struct path *path)
{
return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
dentry->d_op->d_manage(dentry, true) : 0;
return (path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
path->dentry->d_op->d_manage(path, true) : 0;
}
/*
......@@ -1282,7 +1282,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
* Don't forget we might have a non-mountpoint managed dentry
* that wants to block transit.
*/
switch (managed_dentry_rcu(path->dentry)) {
switch (managed_dentry_rcu(path)) {
case -ECHILD:
default:
return false;
......@@ -1392,8 +1392,7 @@ int follow_down(struct path *path)
if (managed & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage(
path->dentry, false);
ret = path->dentry->d_op->d_manage(path, false);
if (ret < 0)
return ret == -EISDIR ? 0 : ret;
}
......
......@@ -1159,6 +1159,35 @@ struct vfsmount *mntget(struct vfsmount *mnt)
}
EXPORT_SYMBOL(mntget);
/* path_is_mountpoint() - Check if path is a mount in the current
* namespace.
*
* d_mountpoint() can only be used reliably to establish if a dentry is
* not mounted in any namespace and that common case is handled inline.
* d_mountpoint() isn't aware of the possibility there may be multiple
* mounts using a given dentry in a different namespace. This function
* checks if the passed in path is a mountpoint rather than the dentry
* alone.
*/
bool path_is_mountpoint(const struct path *path)
{
unsigned seq;
bool res;
if (!d_mountpoint(path->dentry))
return false;
rcu_read_lock();
do {
seq = read_seqbegin(&mount_lock);
res = __path_is_mountpoint(path);
} while (read_seqretry(&mount_lock, seq));
rcu_read_unlock();
return res;
}
EXPORT_SYMBOL(path_is_mountpoint);
struct vfsmount *mnt_clone_internal(const struct path *path)
{
struct mount *p;
......
......@@ -139,7 +139,7 @@ struct dentry_operations {
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
} ____cacheline_aligned;
......@@ -254,7 +254,7 @@ extern struct dentry *d_find_alias(struct inode *);
extern void d_prune_aliases(struct inode *);
/* test whether we have any submounts in a subdir tree */
extern int have_submounts(struct dentry *);
extern int path_has_submounts(const struct path *);
/*
* This adds the entry to the hash queues.
......
......@@ -98,4 +98,6 @@ extern dev_t name_to_dev_t(const char *name);
extern unsigned int sysctl_mount_max;
extern bool path_is_mountpoint(const struct path *path);
#endif /* _LINUX_MOUNT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment