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