From 00cd8dd3bf95f2cc8435b4cac01d9995635c6d0b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 10 Jun 2012 17:13:09 -0400 Subject: stop passing nameidata to ->lookup() Just the flags; only NFS cares even about that, but there are legitimate uses for such argument. And getting rid of that completely would require splitting ->lookup() into a couple of methods (at least), so let's leave that alone for now... Signed-off-by: Al Viro --- fs/autofs4/root.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 75e5f1c8e02..e7396cfdb10 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -32,7 +32,7 @@ static long autofs4_root_ioctl(struct file *,unsigned int,unsigned long); static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); #endif static int autofs4_dir_open(struct inode *inode, struct file *file); -static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); +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 void autofs4_dentry_release(struct dentry *); @@ -458,7 +458,7 @@ int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) } /* Lookups in the root directory */ -static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct autofs_sb_info *sbi; struct autofs_info *ino; -- cgit v1.2.3 From 765927b2d508712d320c8934db963bbe14c3fcec Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 26 Jun 2012 21:58:53 +0400 Subject: switch dentry_open() to struct path, make it grab references itself Signed-off-by: Al Viro --- fs/autofs4/dev-ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index aa9103f8f01..abf645c1703 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -257,8 +257,8 @@ static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) * corresponding to the autofs fs we want to open. */ - filp = dentry_open(path.dentry, path.mnt, O_RDONLY, - current_cred()); + filp = dentry_open(&path, O_RDONLY, current_cred()); + path_put(&path); if (IS_ERR(filp)) { err = PTR_ERR(filp); goto out; -- cgit v1.2.3 From a45440f05e9ebc26f2a375df911823fdef5b5281 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 6 Aug 2012 09:37:47 +0800 Subject: autofs4 - fix get_next_positive_subdir() Following a report of a crash during an automount expire I found that the locking in fs/autofs4/expire.c:get_next_positive_subdir() was wrong. Not only is the locking wrong but the function is more complex than it needs to be. The function is meant to calculate (and dget) the next entry in the list of directories contained in the root of an autofs mount point (an autofs indirect mount to be precise). The main problem was that the d_lock of the owner of the list was not being taken when walking the list, which lead to list corruption under load. The only other lock that needs to be taken is against the next dentry candidate so it can be checked for usability. Signed-off-by: Ian Kent Signed-off-by: Linus Torvalds --- fs/autofs4/expire.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 1feb68ecef9..8c0e56d9293 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -94,25 +94,21 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev, { struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb); struct list_head *next; - struct dentry *p, *q; + struct dentry *q; spin_lock(&sbi->lookup_lock); + spin_lock(&root->d_lock); - if (prev == NULL) { - spin_lock(&root->d_lock); + if (prev) + next = prev->d_u.d_child.next; + else { prev = dget_dlock(root); next = prev->d_subdirs.next; - p = prev; - goto start; } - p = prev; - spin_lock(&p->d_lock); -again: - next = p->d_u.d_child.next; -start: +cont: if (next == &root->d_subdirs) { - spin_unlock(&p->d_lock); + spin_unlock(&root->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); return NULL; @@ -121,16 +117,15 @@ start: q = list_entry(next, struct dentry, d_u.d_child); spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); - /* Negative dentry - try next */ - if (!simple_positive(q)) { - spin_unlock(&p->d_lock); - lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_); - p = q; - goto again; + /* Already gone or negative dentry (under construction) - try next */ + if (q->d_count == 0 || !simple_positive(q)) { + spin_unlock(&q->d_lock); + next = q->d_u.d_child.next; + goto cont; } dget_dlock(q); spin_unlock(&q->d_lock); - spin_unlock(&p->d_lock); + spin_unlock(&root->d_lock); spin_unlock(&sbi->lookup_lock); dput(prev); -- cgit v1.2.3 From d807ff838f48e7778996e577e2a57a5796c32e84 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 17 Aug 2012 11:09:04 +0800 Subject: autofs4 - fix expire check In some cases when an autofs indirect mount is contained in a file system that is marked as shared (such as when systemd does the equivalent of "mount --make-rshared /" early in the boot), mounts stop expiring. When this happens the first expiry check on a mountpoint dentry in autofs_expire_indirect() sees a mountpoint dentry with a higher than minimal reference count. Consequently the dentry is condidered busy and the actual expiry check is never done. This particular check was originally meant as an optimisation to detect a path walk in progress but with the addition of rcu-walk it can be ineffective anyway. Removing the test allows automounts to expire again since the actual expire check doesn't rely on the dentry reference count. Signed-off-by: Ian Kent Signed-off-by: Linus Torvalds --- fs/autofs4/expire.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 8c0e56d9293..842d00048a6 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -399,11 +399,6 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); - /* Path walk currently on this dentry? */ - ino_count = atomic_read(&ino->count) + 2; - if (dentry->d_count > ino_count) - goto next; - /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) goto next; -- cgit v1.2.3 From c921b40d6201f7ec7b1edf7ea9a844f93e1a27f4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 12 Aug 2012 18:04:37 -0400 Subject: autofs4: don't open-code fd_install() The only difference between autofs_dev_ioctl_fd_install() and fd_install() is __set_close_on_exec() done by the latter. Just use get_unused_fd_flags(O_CLOEXEC) to allocate the descriptor and be done with that... Signed-off-by: Al Viro --- fs/autofs4/dev-ioctl.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index abf645c1703..a16214109d3 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -221,20 +221,6 @@ static int test_by_type(struct path *path, void *p) return ino && ino->sbi->type & *(unsigned *)p; } -static void autofs_dev_ioctl_fd_install(unsigned int fd, struct file *file) -{ - struct files_struct *files = current->files; - struct fdtable *fdt; - - spin_lock(&files->file_lock); - fdt = files_fdtable(files); - BUG_ON(fdt->fd[fd] != NULL); - rcu_assign_pointer(fdt->fd[fd], file); - __set_close_on_exec(fd, fdt); - spin_unlock(&files->file_lock); -} - - /* * Open a file descriptor on the autofs mount point corresponding * to the given path and device number (aka. new_encode_dev(sb->s_dev)). @@ -243,7 +229,7 @@ static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) { int err, fd; - fd = get_unused_fd(); + fd = get_unused_fd_flags(O_CLOEXEC); if (likely(fd >= 0)) { struct file *filp; struct path path; @@ -264,7 +250,7 @@ static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) goto out; } - autofs_dev_ioctl_fd_install(fd, filp); + fd_install(fd, filp); } return fd; -- cgit v1.2.3 From cb0942b81249798e15c3f04eee2946ef543e8115 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 27 Aug 2012 14:48:26 -0400 Subject: make get_file() return its argument simplifies a bunch of callers... Signed-off-by: Al Viro --- fs/autofs4/waitq.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index da8876d38a7..dce436e595c 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -175,8 +175,7 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, return; } - pipe = sbi->pipe; - get_file(pipe); + pipe = get_file(sbi->pipe); mutex_unlock(&sbi->wq_mutex); -- cgit v1.2.3 From 49999ab27eab6289a8e4f450e148bdab521361b2 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Thu, 11 Oct 2012 08:00:33 +0800 Subject: autofs4 - fix reset pending flag on mount fail In autofs4_d_automount(), if a mount fail occurs the AUTOFS_INF_PENDING mount pending flag is not cleared. One effect of this is when using the "browse" option, directory entry attributes show up with all "?"s due to the incorrect callback and subsequent failure return (when in fact no callback should be made). Signed-off-by: Ian Kent Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- fs/autofs4/root.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index e7396cfdb10..91b11650722 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -392,10 +392,12 @@ static struct vfsmount *autofs4_d_automount(struct path *path) ino->flags |= AUTOFS_INF_PENDING; spin_unlock(&sbi->fs_lock); status = autofs4_mount_wait(dentry); - if (status) - return ERR_PTR(status); spin_lock(&sbi->fs_lock); ino->flags &= ~AUTOFS_INF_PENDING; + if (status) { + spin_unlock(&sbi->fs_lock); + return ERR_PTR(status); + } } done: if (!(ino->flags & AUTOFS_INF_EXPIRING)) { -- cgit v1.2.3 From 45634cd8cb6541523227753944c7417ac3d20f94 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:18:52 -0800 Subject: userns: Support autofs4 interacing with multiple user namespaces Use kuid_t and kgid_t in struct autofs_info and struct autofs_wait_queue. When creating directories and symlinks default the uid and gid of the mount requester to the global root uid and gid. autofs4_wait will update these fields when a mount is requested. When generating autofsv5 packets report the uid and gid of the mount requestor in user namespace of the process that opened the pipe, reporting unmapped uids and gids as overflowuid and overflowgid. In autofs_dev_ioctl_requester return the uid and gid of the last mount requester converted into the calling processes user namespace. When the uid or gid don't map return overflowuid and overflowgid as appropriate, allowing failure to find a mount requester to be distinguished from failure to map a mount requester. The uid and gid mount options specifying the user and group of the root autofs inode are converted into kuid and kgid as they are parsed defaulting to the current uid and current gid of the process that mounts autofs. Mounting of autofs for the present remains confined to processes in the initial user namespace. Cc: Ian Kent Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/autofs4/autofs_i.h | 8 ++++---- fs/autofs4/dev-ioctl.c | 4 ++-- fs/autofs4/inode.c | 24 +++++++++++++++--------- fs/autofs4/waitq.c | 5 +++-- 4 files changed, 24 insertions(+), 17 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 908e1845541..b785e770795 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -74,8 +74,8 @@ struct autofs_info { unsigned long last_used; atomic_t count; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; }; #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ @@ -89,8 +89,8 @@ struct autofs_wait_queue { struct qstr name; u32 dev; u64 ino; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; pid_t pid; pid_t tgid; /* This is for status reporting upon return */ diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index a16214109d3..9f68a37bb2b 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -437,8 +437,8 @@ static int autofs_dev_ioctl_requester(struct file *fp, err = 0; autofs4_expire_wait(path.dentry); spin_lock(&sbi->fs_lock); - param->requester.uid = ino->uid; - param->requester.gid = ino->gid; + param->requester.uid = from_kuid_munged(current_user_ns(), ino->uid); + param->requester.gid = from_kgid_munged(current_user_ns(), ino->gid); spin_unlock(&sbi->fs_lock); } path_put(&path); diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 8a4fed8ead3..b104726e2d0 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -36,8 +36,8 @@ struct autofs_info *autofs4_new_ino(struct autofs_sb_info *sbi) void autofs4_clean_ino(struct autofs_info *ino) { - ino->uid = 0; - ino->gid = 0; + ino->uid = GLOBAL_ROOT_UID; + ino->gid = GLOBAL_ROOT_GID; ino->last_used = jiffies; } @@ -79,10 +79,12 @@ static int autofs4_show_options(struct seq_file *m, struct dentry *root) return 0; seq_printf(m, ",fd=%d", sbi->pipefd); - if (root_inode->i_uid != 0) - seq_printf(m, ",uid=%u", root_inode->i_uid); - if (root_inode->i_gid != 0) - seq_printf(m, ",gid=%u", root_inode->i_gid); + if (!uid_eq(root_inode->i_uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, root_inode->i_uid)); + if (!gid_eq(root_inode->i_gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, root_inode->i_gid)); seq_printf(m, ",pgrp=%d", sbi->oz_pgrp); seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ); seq_printf(m, ",minproto=%d", sbi->min_proto); @@ -126,7 +128,7 @@ static const match_table_t tokens = { {Opt_err, NULL} }; -static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, +static int parse_options(char *options, int *pipefd, kuid_t *uid, kgid_t *gid, pid_t *pgrp, unsigned int *type, int *minproto, int *maxproto) { char *p; @@ -159,12 +161,16 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, case Opt_uid: if (match_int(args, &option)) return 1; - *uid = option; + *uid = make_kuid(current_user_ns(), option); + if (!uid_valid(*uid)) + return 1; break; case Opt_gid: if (match_int(args, &option)) return 1; - *gid = option; + *gid = make_kgid(current_user_ns(), option); + if (!gid_valid(*gid)) + return 1; break; case Opt_pgrp: if (match_int(args, &option)) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index dce436e595c..03bc1d347d8 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -154,6 +154,7 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, case autofs_ptype_expire_direct: { struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; + struct user_namespace *user_ns = sbi->pipe->f_cred->user_ns; pktsz = sizeof(*packet); @@ -163,8 +164,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, packet->name[wq->name.len] = '\0'; packet->dev = wq->dev; packet->ino = wq->ino; - packet->uid = wq->uid; - packet->gid = wq->gid; + packet->uid = from_kuid_munged(user_ns, wq->uid); + packet->gid = from_kgid_munged(user_ns, wq->gid); packet->pid = wq->pid; packet->tgid = wq->tgid; break; -- cgit v1.2.3 From f55fb0c24386cee8b78f60d60186716bd0909493 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 14 Dec 2012 10:23:23 +0800 Subject: autofs4 - dont clear DCACHE_NEED_AUTOMOUNT on rootless mount The DCACHE_NEED_AUTOMOUNT flag is cleared on mount and set on expire for autofs rootless multi-mount dentrys to prevent unnecessary calls to ->d_automount(). Since DCACHE_MANAGE_TRANSIT is always set on autofs dentrys ->d_managed() is always called so the check can be done in ->d_manage() without the need to change the flag. This still avoids unnecessary calls to ->d_automount(), adds negligible overhead and eliminates a seriously ugly check in the expire code. Signed-off-by: Ian Kent Signed-off-by: Linus Torvalds --- fs/autofs4/expire.c | 9 -------- fs/autofs4/root.c | 61 +++++++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 34 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 842d00048a6..01443ce43ee 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -548,15 +548,6 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, spin_lock(&sbi->fs_lock); ino->flags &= ~AUTOFS_INF_EXPIRING; - spin_lock(&dentry->d_lock); - if (!ret) { - if ((IS_ROOT(dentry) || - (autofs_type_indirect(sbi->type) && - IS_ROOT(dentry->d_parent))) && - !(dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) - __managed_dentry_set_automount(dentry); - } - spin_unlock(&dentry->d_lock); complete_all(&ino->expire_complete); spin_unlock(&sbi->fs_lock); dput(dentry); diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 91b11650722..30a6ab66e99 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -355,7 +355,6 @@ static struct vfsmount *autofs4_d_automount(struct path *path) status = autofs4_mount_wait(dentry); if (status) return ERR_PTR(status); - spin_lock(&sbi->fs_lock); goto done; } @@ -364,8 +363,11 @@ static struct vfsmount *autofs4_d_automount(struct path *path) * having d_mountpoint() true, so there's no need to call back * to the daemon. */ - if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) + if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) { + spin_unlock(&sbi->fs_lock); goto done; + } + if (!d_mountpoint(dentry)) { /* * It's possible that user space hasn't removed directories @@ -379,8 +381,10 @@ static struct vfsmount *autofs4_d_automount(struct path *path) * require user space behave. */ if (sbi->version > 4) { - if (have_submounts(dentry)) + if (have_submounts(dentry)) { + spin_unlock(&sbi->fs_lock); goto done; + } } else { spin_lock(&dentry->d_lock); if (!list_empty(&dentry->d_subdirs)) { @@ -399,28 +403,8 @@ static struct vfsmount *autofs4_d_automount(struct path *path) return ERR_PTR(status); } } -done: - if (!(ino->flags & AUTOFS_INF_EXPIRING)) { - /* - * Any needed mounting has been completed and the path - * updated so clear DCACHE_NEED_AUTOMOUNT so we don't - * call ->d_automount() on rootless multi-mounts since - * it can lead to an incorrect ELOOP error return. - * - * Only clear DMANAGED_AUTOMOUNT for rootless multi-mounts and - * symlinks as in all other cases the dentry will be covered by - * an actual mount so ->d_automount() won't be called during - * the follow. - */ - spin_lock(&dentry->d_lock); - if ((!d_mountpoint(dentry) && - !list_empty(&dentry->d_subdirs)) || - (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode))) - __managed_dentry_clear_automount(dentry); - spin_unlock(&dentry->d_lock); - } spin_unlock(&sbi->fs_lock); - +done: /* Mount succeeded, check if we ended up with a new dentry */ dentry = autofs4_mountpoint_changed(path); if (!dentry) @@ -432,6 +416,8 @@ done: int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); + int status; DPRINTK("dentry=%p %.*s", dentry, dentry->d_name.len, dentry->d_name.name); @@ -456,7 +442,32 @@ int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) * This dentry may be under construction so wait on mount * completion. */ - return autofs4_mount_wait(dentry); + status = autofs4_mount_wait(dentry); + if (status) + return status; + + spin_lock(&sbi->fs_lock); + /* + * If the dentry has been selected for expire while we slept + * on the lock then it might go away. We'll deal with that in + * ->d_automount() and wait on a new mount if the expire + * succeeds or return here if it doesn't (since there's no + * mount to follow with a rootless multi-mount). + */ + if (!(ino->flags & AUTOFS_INF_EXPIRING)) { + /* + * Any needed mounting has been completed and the path + * updated so check if this is a rootless multi-mount so + * we can avoid needless calls ->d_automount() and avoid + * an incorrect ELOOP error return. + */ + if ((!d_mountpoint(dentry) && !simple_empty(dentry)) || + (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode))) + status = -EISDIR; + } + spin_unlock(&sbi->fs_lock); + + return status; } /* Lookups in the root directory */ -- cgit v1.2.3 From 0259cb02c4004d3088b0999799f8f5c8801f6b97 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 14 Dec 2012 10:23:29 +0800 Subject: autofs4 - use simple_empty() for empty directory check For direct (and offset) mounts, if an automounted mount is manually umounted the trigger mount dentry can appear non-empty causing it to not trigger mounts. This can also happen if there is a file handle leak in a user space automounting application. This happens because, when a ioctl control file handle is opened on the mount, a cursor dentry is created which causes list_empty() to see the dentry as non-empty. Since there is a case where listing the directory of these dentrys is needed, the use of dcache_dir_*() functions for .open() and .release() is needed. Consequently simple_empty() must be used instead of list_empty() when checking for an empty directory. Signed-off-by: Ian Kent Signed-off-by: Linus Torvalds --- fs/autofs4/root.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'fs/autofs4') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 30a6ab66e99..c93447604da 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -124,13 +124,10 @@ static int autofs4_dir_open(struct inode *inode, struct file *file) * it. */ spin_lock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { - spin_unlock(&dentry->d_lock); + if (!d_mountpoint(dentry) && simple_empty(dentry)) { spin_unlock(&sbi->lookup_lock); return -ENOENT; } - spin_unlock(&dentry->d_lock); spin_unlock(&sbi->lookup_lock); out: @@ -386,12 +383,8 @@ static struct vfsmount *autofs4_d_automount(struct path *path) goto done; } } else { - spin_lock(&dentry->d_lock); - if (!list_empty(&dentry->d_subdirs)) { - spin_unlock(&dentry->d_lock); + if (!simple_empty(dentry)) goto done; - } - spin_unlock(&dentry->d_lock); } ino->flags |= AUTOFS_INF_PENDING; spin_unlock(&sbi->fs_lock); @@ -610,9 +603,7 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) spin_lock(&sbi->lookup_lock); __autofs4_add_expiring(dentry); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); + d_drop(dentry); spin_unlock(&sbi->lookup_lock); return 0; @@ -683,15 +674,12 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) return -EACCES; spin_lock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - if (!list_empty(&dentry->d_subdirs)) { - spin_unlock(&dentry->d_lock); + if (!simple_empty(dentry)) { spin_unlock(&sbi->lookup_lock); return -ENOTEMPTY; } __autofs4_add_expiring(dentry); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); + d_drop(dentry); spin_unlock(&sbi->lookup_lock); if (sbi->version < 5) -- cgit v1.2.3