autofs-5.0.4 - update kernel patches From: Ian Kent Update the kernel patches with the latest kernel chenges. --- CHANGELOG | 1 patches/autofs4-2.6.10-v5-update-20081027.patch | 3096 ---------------- patches/autofs4-2.6.11-v5-update-20081027.patch | 3096 ---------------- patches/autofs4-2.6.12-v5-update-20081027.patch | 3115 ---------------- patches/autofs4-2.6.13-v5-update-20081027.patch | 3104 ---------------- patches/autofs4-2.6.14-v5-update-20081027.patch | 3146 ---------------- patches/autofs4-2.6.15-v5-update-20081027.patch | 3157 ---------------- patches/autofs4-2.6.16-v5-update-20081027.patch | 3170 ---------------- patches/autofs4-2.6.17-v5-update-20081027.patch | 2201 ----------- patches/autofs4-2.6.18-v5-update-20081027.patch | 2186 ----------- patches/autofs4-2.6.18-v5-update-20090903.patch | 3928 ++++++++++++++++++++ patches/autofs4-2.6.19-v5-update-20081027.patch | 1909 ---------- patches/autofs4-2.6.19-v5-update-20090903.patch | 3667 +++++++++++++++++++ patches/autofs4-2.6.20-v5-update-20081027.patch | 1857 --------- patches/autofs4-2.6.20-v5-update-20090903.patch | 3621 ++++++++++++++++++ patches/autofs4-2.6.21-v5-update-20090903.patch | 3564 ++++++++++++++++++ patches/autofs4-2.6.22-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.22-v5-update-20081027.patch | 1799 --------- patches/autofs4-2.6.22-v5-update-20090903.patch | 3564 ++++++++++++++++++ patches/autofs4-2.6.22.17-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.22.17-v5-update-20081027.patch | 1799 --------- patches/autofs4-2.6.22.17-v5-update-20090903.patch | 3564 ++++++++++++++++++ patches/autofs4-2.6.23-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.23-v5-update-20081027.patch | 1774 --------- patches/autofs4-2.6.23-v5-update-20090903.patch | 3539 ++++++++++++++++++ patches/autofs4-2.6.24-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.24-v5-update-20081027.patch | 1774 --------- patches/autofs4-2.6.24-v5-update-20090903.patch | 3539 ++++++++++++++++++ patches/autofs4-2.6.24.4-dev-ioctl-20081029.patch | 1884 ---------- patches/autofs4-2.6.24.4-v5-update-20081027.patch | 1774 --------- patches/autofs4-2.6.24.4-v5-update-20090903.patch | 3505 ++++++++++++++++++ patches/autofs4-2.6.25-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.25-v5-update-20081027.patch | 1776 --------- patches/autofs4-2.6.25-v5-update-20090903.patch | 3541 ++++++++++++++++++ patches/autofs4-2.6.26-dev-ioctl-20081029.patch | 1918 ---------- patches/autofs4-2.6.26-v5-update-20081027.patch | 1754 --------- patches/autofs4-2.6.26-v5-update-20090903.patch | 3519 ++++++++++++++++++ patches/autofs4-2.6.27-dev-ioctl-20081029.patch | 1919 ---------- patches/autofs4-2.6.27-v5-update-20081027.patch | 148 - patches/autofs4-2.6.27-v5-update-20090903.patch | 2041 ++++++++++ patches/autofs4-2.6.28-v5-update-20090903.patch | 908 +++++ patches/autofs4-2.6.29-v5-update-20090903.patch | 240 + patches/autofs4-2.6.9-v5-update-20081027.patch | 3119 ---------------- 43 files changed, 42741 insertions(+), 61065 deletions(-) delete mode 100644 patches/autofs4-2.6.10-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.11-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.12-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.13-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.14-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.15-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.16-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.17-v5-update-20081027.patch delete mode 100644 patches/autofs4-2.6.18-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.18-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.19-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.19-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.20-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.20-v5-update-20090903.patch create mode 100644 patches/autofs4-2.6.21-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.22-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.22-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.22-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.22.17-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.22.17-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.22.17-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.23-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.23-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.23-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.24-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.24-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.24-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.24.4-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.24.4-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.24.4-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.25-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.25-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.25-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.26-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.26-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.26-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.27-dev-ioctl-20081029.patch delete mode 100644 patches/autofs4-2.6.27-v5-update-20081027.patch create mode 100644 patches/autofs4-2.6.27-v5-update-20090903.patch create mode 100644 patches/autofs4-2.6.28-v5-update-20090903.patch create mode 100644 patches/autofs4-2.6.29-v5-update-20090903.patch delete mode 100644 patches/autofs4-2.6.9-v5-update-20081027.patch diff --git a/CHANGELOG b/CHANGELOG index 929a21f..224ff5e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,6 +62,7 @@ - fix libxml2 non-thread-safe calls. - fix direct map cache locking. - fix dont umount existing direct mount on reread. +- update kernel patches. 4/11/2008 autofs-5.0.4 ----------------------- diff --git a/patches/autofs4-2.6.10-v5-update-20081027.patch b/patches/autofs4-2.6.10-v5-update-20081027.patch deleted file mode 100644 index ea00b55..0000000 --- a/patches/autofs4-2.6.10-v5-update-20081027.patch +++ /dev/null @@ -1,3096 +0,0 @@ -Index: linux-2.6.10/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.10/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,35 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; -+ spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -127,8 +141,13 @@ static inline int autofs4_ispending(stru - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) -+ return 1; -+ -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; -+ -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -142,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -153,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -163,23 +185,39 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); - -+static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **dentry) -+{ -+ int res = 0; -+ -+ while (d_mountpoint(*dentry)) { -+ int followed = follow_down(mnt, dentry); -+ if (!followed) -+ break; -+ res = 1; -+ } -+ return res; -+} -+ -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -191,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.10/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/expire.c -+++ linux-2.6.10/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -59,91 +59,160 @@ static int autofs4_check_mount(struct vf - if (!follow_down(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -151,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; -+ -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -224,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -240,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -248,58 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- expired = dentry; -- break; -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -+ expired = dentry; -+ goto found; - } -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -309,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -325,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -342,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.10/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/inode.c -+++ linux-2.6.10/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,9 +14,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include "autofs_i.h" - #include - -@@ -40,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -65,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -76,26 +93,121 @@ void autofs4_free_ino(struct autofs_info - kfree(ino); - } - --static void autofs4_put_super(struct super_block *sb) -+/* -+ * Deal with the infamous "Busy inodes after umount ..." message. -+ * -+ * Clean up the dentry tree. This happens with autofs if the user -+ * space program goes away due to a SIGKILL, SIGSEGV etc. -+ */ -+static void autofs4_force_release(struct autofs_sb_info *sbi) -+{ -+ struct dentry *this_parent = sbi->sb->s_root; -+ struct list_head *next; -+ -+ if (!sbi->sb->s_root) -+ return; -+ -+ spin_lock(&dcache_lock); -+repeat: -+ next = this_parent->d_subdirs.next; -+resume: -+ while (next != &this_parent->d_subdirs) { -+ struct dentry *dentry = list_entry(next, struct dentry, d_child); -+ -+ /* Negative dentry - don`t care */ -+ if (!simple_positive(dentry)) { -+ next = next->next; -+ continue; -+ } -+ -+ if (!list_empty(&dentry->d_subdirs)) { -+ this_parent = dentry; -+ goto repeat; -+ } -+ -+ next = next->next; -+ spin_unlock(&dcache_lock); -+ -+ DPRINTK("dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ } -+ -+ if (this_parent != sbi->sb->s_root) { -+ struct dentry *dentry = this_parent; -+ -+ next = this_parent->d_child.next; -+ this_parent = this_parent->d_parent; -+ spin_unlock(&dcache_lock); -+ DPRINTK("parent dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ goto resume; -+ } -+ spin_unlock(&dcache_lock); -+ shrink_dcache_sb(sbi->sb); -+} -+ -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - -+ /* Clean up and release dangling references */ -+ autofs4_force_release(sbi); -+ -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -104,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -162,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -188,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -199,14 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); -+ spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -219,38 +356,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -263,6 +408,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -283,8 +430,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.10/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/waitq.c -+++ linux-2.6.10/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,44 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ return ret; - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -205,41 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; - } -+ -+ DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); -+ -+ /* autofs4_notify_daemon() may block */ -+ autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - up(&sbi->wq_sem); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } -- -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -250,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -263,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -275,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.10/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.10.orig/include/linux/auto_fs4.h -+++ linux-2.6.10/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 5 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.10/fs/autofs4/root.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/root.c -+++ linux-2.6.10/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static int autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,135 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -+ return -ENOENT; - } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -+ spin_unlock(&dcache_lock); - -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -317,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -348,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -369,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -419,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -493,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -516,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -526,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -562,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -575,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -587,7 +697,13 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +712,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -605,11 +725,19 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - __d_drop(dentry); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -623,6 +751,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -632,11 +761,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -645,6 +784,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -684,51 +827,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -785,11 +890,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.10/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs/dirhash.c -+++ linux-2.6.10/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.10/fs/namespace.c -=================================================================== ---- linux-2.6.10.orig/fs/namespace.c -+++ linux-2.6.10/fs/namespace.c -@@ -308,9 +308,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -330,9 +330,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.10/fs/namei.c -=================================================================== ---- linux-2.6.10.orig/fs/namei.c -+++ linux-2.6.10/fs/namei.c -@@ -304,6 +304,29 @@ void path_release_on_umount(struct namei - _mntput(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -318,12 +341,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -416,10 +436,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -651,12 +670,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -@@ -762,6 +781,11 @@ int fastcall link_path_walk(const char * - - if (inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -@@ -816,6 +840,11 @@ last_component: - if ((lookup_flags & LOOKUP_FOLLOW) - && inode && inode->i_op && inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -Index: linux-2.6.10/fs/autofs/init.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs/init.c -+++ linux-2.6.10/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.10/fs/autofs/inode.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs/inode.c -+++ linux-2.6.10/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -178,6 +189,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -196,6 +208,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.10/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.10.orig/fs/autofs/autofs_i.h -+++ linux-2.6.10/fs/autofs/autofs_i.h -@@ -150,6 +150,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.10/fs/autofs4/init.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs4/init.c -+++ linux-2.6.10/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.10/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.10.orig/fs/autofs/waitq.c -+++ linux-2.6.10/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.10/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.10.orig/include/linux/compat_ioctl.h -+++ linux-2.6.10/include/linux/compat_ioctl.h -@@ -563,8 +563,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.11-v5-update-20081027.patch b/patches/autofs4-2.6.11-v5-update-20081027.patch deleted file mode 100644 index 9b7aeeb..0000000 --- a/patches/autofs4-2.6.11-v5-update-20081027.patch +++ /dev/null @@ -1,3096 +0,0 @@ -Index: linux-2.6.11/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.11/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,35 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; -+ spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -127,8 +141,13 @@ static inline int autofs4_ispending(stru - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) -+ return 1; -+ -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; -+ -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -142,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -153,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -163,23 +185,39 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); - -+static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **dentry) -+{ -+ int res = 0; -+ -+ while (d_mountpoint(*dentry)) { -+ int followed = follow_down(mnt, dentry); -+ if (!followed) -+ break; -+ res = 1; -+ } -+ return res; -+} -+ -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -191,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.11/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/expire.c -+++ linux-2.6.11/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -59,91 +59,160 @@ static int autofs4_check_mount(struct vf - if (!follow_down(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -151,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; -+ -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -224,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -240,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -248,58 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- expired = dentry; -- break; -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -+ expired = dentry; -+ goto found; - } -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -309,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -325,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -342,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.11/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/inode.c -+++ linux-2.6.11/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,9 +14,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include "autofs_i.h" - #include - -@@ -40,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -65,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -76,26 +93,121 @@ void autofs4_free_ino(struct autofs_info - kfree(ino); - } - --static void autofs4_put_super(struct super_block *sb) -+/* -+ * Deal with the infamous "Busy inodes after umount ..." message. -+ * -+ * Clean up the dentry tree. This happens with autofs if the user -+ * space program goes away due to a SIGKILL, SIGSEGV etc. -+ */ -+static void autofs4_force_release(struct autofs_sb_info *sbi) -+{ -+ struct dentry *this_parent = sbi->sb->s_root; -+ struct list_head *next; -+ -+ if (!sbi->sb->s_root) -+ return; -+ -+ spin_lock(&dcache_lock); -+repeat: -+ next = this_parent->d_subdirs.next; -+resume: -+ while (next != &this_parent->d_subdirs) { -+ struct dentry *dentry = list_entry(next, struct dentry, d_child); -+ -+ /* Negative dentry - don`t care */ -+ if (!simple_positive(dentry)) { -+ next = next->next; -+ continue; -+ } -+ -+ if (!list_empty(&dentry->d_subdirs)) { -+ this_parent = dentry; -+ goto repeat; -+ } -+ -+ next = next->next; -+ spin_unlock(&dcache_lock); -+ -+ DPRINTK("dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ } -+ -+ if (this_parent != sbi->sb->s_root) { -+ struct dentry *dentry = this_parent; -+ -+ next = this_parent->d_child.next; -+ this_parent = this_parent->d_parent; -+ spin_unlock(&dcache_lock); -+ DPRINTK("parent dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ goto resume; -+ } -+ spin_unlock(&dcache_lock); -+ shrink_dcache_sb(sbi->sb); -+} -+ -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - -+ /* Clean up and release dangling references */ -+ autofs4_force_release(sbi); -+ -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -104,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -162,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -188,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -199,14 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); -+ spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -220,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -264,6 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -284,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.11/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/waitq.c -+++ linux-2.6.11/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,44 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ return ret; - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -205,41 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; - } -+ -+ DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); -+ -+ /* autofs4_notify_daemon() may block */ -+ autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - up(&sbi->wq_sem); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } -- -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -250,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -263,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -275,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.11/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.11.orig/include/linux/auto_fs4.h -+++ linux-2.6.11/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 5 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.11/fs/autofs4/root.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/root.c -+++ linux-2.6.11/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static int autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,135 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -+ return -ENOENT; - } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -+ spin_unlock(&dcache_lock); - -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -317,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -348,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -369,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -419,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -493,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -516,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -526,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -562,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -575,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -587,7 +697,13 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +712,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -605,11 +725,19 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - __d_drop(dentry); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -623,6 +751,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -632,11 +761,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -645,6 +784,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -684,51 +827,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -785,11 +890,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.11/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs/dirhash.c -+++ linux-2.6.11/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.11/fs/namespace.c -=================================================================== ---- linux-2.6.11.orig/fs/namespace.c -+++ linux-2.6.11/fs/namespace.c -@@ -308,9 +308,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -330,9 +330,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.11/fs/namei.c -=================================================================== ---- linux-2.6.11.orig/fs/namei.c -+++ linux-2.6.11/fs/namei.c -@@ -306,6 +306,29 @@ void path_release_on_umount(struct namei - _mntput(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -320,12 +343,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -418,10 +438,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -662,12 +681,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -@@ -773,6 +792,11 @@ int fastcall link_path_walk(const char * - - if (inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -@@ -827,6 +851,11 @@ last_component: - if ((lookup_flags & LOOKUP_FOLLOW) - && inode && inode->i_op && inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -Index: linux-2.6.11/fs/autofs/init.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs/init.c -+++ linux-2.6.11/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.11/fs/autofs/inode.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs/inode.c -+++ linux-2.6.11/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -179,6 +190,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -197,6 +209,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.11/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.11.orig/fs/autofs/autofs_i.h -+++ linux-2.6.11/fs/autofs/autofs_i.h -@@ -150,6 +150,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.11/fs/autofs4/init.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs4/init.c -+++ linux-2.6.11/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.11/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.11.orig/fs/autofs/waitq.c -+++ linux-2.6.11/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.11/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.11.orig/include/linux/compat_ioctl.h -+++ linux-2.6.11/include/linux/compat_ioctl.h -@@ -565,8 +565,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.12-v5-update-20081027.patch b/patches/autofs4-2.6.12-v5-update-20081027.patch deleted file mode 100644 index 149fac4..0000000 --- a/patches/autofs4-2.6.12-v5-update-20081027.patch +++ /dev/null @@ -1,3115 +0,0 @@ -Index: linux-2.6.12/fs/autofs4/root.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/root.c -+++ linux-2.6.12/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static void *autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,135 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -+ return -ENOENT; - } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -+ spin_unlock(&dcache_lock); - -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -317,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -348,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -369,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -419,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -493,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -516,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -526,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -562,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -575,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -587,7 +697,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +714,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -605,13 +727,21 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -625,6 +755,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -634,11 +765,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -647,6 +788,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -686,51 +831,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -787,11 +894,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.12/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/waitq.c -+++ linux-2.6.12/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,44 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ return ret; - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -205,44 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -- atomic_set(&wq->notified, 1); -- up(&sbi->wq_sem); -- } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); -- kfree(name); -- DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } - -- if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { -- int type = (notify == NFY_MOUNT ? -- autofs_ptype_missing : autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; -+ } - -- DPRINTK(("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify)); -+ DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } else { -+ wq->wait_ctr++; -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -- } -- -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -253,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -266,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -278,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.12/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.12/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,37 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t notified; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -128,18 +140,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -153,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -164,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -174,23 +185,39 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); - -+static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **dentry) -+{ -+ int res = 0; -+ -+ while (d_mountpoint(*dentry)) { -+ int followed = follow_down(mnt, dentry); -+ if (!followed) -+ break; -+ res = 1; -+ } -+ return res; -+} -+ -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -202,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.12/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/inode.c -+++ linux-2.6.12/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,9 +14,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include "autofs_i.h" - #include - -@@ -40,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -65,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -76,26 +93,121 @@ void autofs4_free_ino(struct autofs_info - kfree(ino); - } - --static void autofs4_put_super(struct super_block *sb) -+/* -+ * Deal with the infamous "Busy inodes after umount ..." message. -+ * -+ * Clean up the dentry tree. This happens with autofs if the user -+ * space program goes away due to a SIGKILL, SIGSEGV etc. -+ */ -+static void autofs4_force_release(struct autofs_sb_info *sbi) -+{ -+ struct dentry *this_parent = sbi->sb->s_root; -+ struct list_head *next; -+ -+ if (!sbi->sb->s_root) -+ return; -+ -+ spin_lock(&dcache_lock); -+repeat: -+ next = this_parent->d_subdirs.next; -+resume: -+ while (next != &this_parent->d_subdirs) { -+ struct dentry *dentry = list_entry(next, struct dentry, d_child); -+ -+ /* Negative dentry - don`t care */ -+ if (!simple_positive(dentry)) { -+ next = next->next; -+ continue; -+ } -+ -+ if (!list_empty(&dentry->d_subdirs)) { -+ this_parent = dentry; -+ goto repeat; -+ } -+ -+ next = next->next; -+ spin_unlock(&dcache_lock); -+ -+ DPRINTK("dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ } -+ -+ if (this_parent != sbi->sb->s_root) { -+ struct dentry *dentry = this_parent; -+ -+ next = this_parent->d_child.next; -+ this_parent = this_parent->d_parent; -+ spin_unlock(&dcache_lock); -+ DPRINTK("parent dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ goto resume; -+ } -+ spin_unlock(&dcache_lock); -+ shrink_dcache_sb(sbi->sb); -+} -+ -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - -+ /* Clean up and release dangling references */ -+ autofs4_force_release(sbi); -+ -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -104,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -162,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -188,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -199,15 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -221,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -265,6 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -285,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.12/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/expire.c -+++ linux-2.6.12/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -59,95 +59,160 @@ static int autofs4_check_mount(struct vf - if (!follow_down(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -- -- /* Is someone visiting anywhere in the tree ? */ -- if (may_umount_tree(mnt)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -155,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; -+ -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -217,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -228,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -244,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -252,66 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -- -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -321,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -337,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -354,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.12/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.12.orig/include/linux/auto_fs4.h -+++ linux-2.6.12/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 6 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.12/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs/dirhash.c -+++ linux-2.6.12/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.12/fs/namespace.c -=================================================================== ---- linux-2.6.12.orig/fs/namespace.c -+++ linux-2.6.12/fs/namespace.c -@@ -308,9 +308,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -330,9 +330,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.12/fs/namei.c -=================================================================== ---- linux-2.6.12.orig/fs/namei.c -+++ linux-2.6.12/fs/namei.c -@@ -317,6 +317,29 @@ void path_release_on_umount(struct namei - _mntput(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -331,12 +354,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -429,10 +449,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -506,8 +525,15 @@ static inline int __do_follow_link(struc - touch_atime(path->mnt, dentry); - nd_set_link(nd, NULL); - -- if (path->mnt == nd->mnt) -- mntget(path->mnt); -+ if (path->mnt != nd->mnt) { -+ dput(nd->dentry); -+ if (nd->mnt != path->mnt) -+ mntput(nd->mnt); -+ nd->mnt = path->mnt; -+ nd->dentry = path->dentry; -+ dget(dentry); -+ } -+ mntget(path->mnt); - error = dentry->d_inode->i_op->follow_link(dentry, nd); - if (!error) { - char *s = nd_get_link(nd); -@@ -692,12 +718,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.12/fs/autofs/init.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs/init.c -+++ linux-2.6.12/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.12/fs/autofs/inode.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs/inode.c -+++ linux-2.6.12/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -179,6 +190,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -197,6 +209,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.12/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.12.orig/fs/autofs/autofs_i.h -+++ linux-2.6.12/fs/autofs/autofs_i.h -@@ -150,6 +150,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.12/fs/autofs4/init.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs4/init.c -+++ linux-2.6.12/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.12/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.12.orig/fs/autofs/waitq.c -+++ linux-2.6.12/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.12/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.12.orig/include/linux/compat_ioctl.h -+++ linux-2.6.12/include/linux/compat_ioctl.h -@@ -566,8 +566,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.13-v5-update-20081027.patch b/patches/autofs4-2.6.13-v5-update-20081027.patch deleted file mode 100644 index 9ba63dc..0000000 --- a/patches/autofs4-2.6.13-v5-update-20081027.patch +++ /dev/null @@ -1,3104 +0,0 @@ -Index: linux-2.6.13/fs/autofs4/root.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/root.c -+++ linux-2.6.13/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static void *autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,146 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- return -ENOENT; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -- } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -328,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -359,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -430,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -504,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -527,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -537,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -573,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -586,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -598,7 +697,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +714,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -616,13 +727,21 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -636,6 +755,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -645,11 +765,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +788,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -697,51 +831,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -798,11 +894,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.13/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.13/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,38 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t notified; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -129,18 +140,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -154,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -165,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -175,13 +185,6 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); -@@ -199,12 +202,22 @@ static inline int autofs4_follow_mount(s - return res; - } - -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -216,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.13/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/expire.c -+++ linux-2.6.13/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -56,95 +56,163 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } -+ -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -- -- /* Is someone visiting anywhere in the tree ? */ -- if (may_umount_tree(mnt)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -152,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; -+ -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -225,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -241,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -249,66 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -- -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -318,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -334,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -351,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.13/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/inode.c -+++ linux-2.6.13/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -41,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -66,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -85,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -116,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_child.next; -@@ -129,38 +148,66 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ -- if (sbi) -- autofs4_force_release(sbi); -+ autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -169,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -227,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -245,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -253,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -264,16 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -287,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -331,13 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -358,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.13/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/waitq.c -+++ linux-2.6.13/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,51 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -- } -- -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -- kfree(name); -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) - up(&sbi->wq_sem); -- return -ENOENT; -- } -+ kfree(qstr.name); -+ return ret; -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -212,44 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -- atomic_set(&wq->notified, 1); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); -- } else { -- atomic_inc(&wq->wait_ctr); -- up(&sbi->wq_sem); -- kfree(name); -- DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } - -- if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { -- int type = (notify == NFY_MOUNT ? -- autofs_ptype_missing : autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; -+ } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } else { -+ wq->wait_ctr++; -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -- } -- -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -260,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -273,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -285,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.13/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs/dirhash.c -+++ linux-2.6.13/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.13/fs/namespace.c -=================================================================== ---- linux-2.6.13.orig/fs/namespace.c -+++ linux-2.6.13/fs/namespace.c -@@ -308,9 +308,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -330,9 +330,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.13/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.13.orig/include/linux/auto_fs4.h -+++ linux-2.6.13/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 7 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.13/fs/namei.c -=================================================================== ---- linux-2.6.13.orig/fs/namei.c -+++ linux-2.6.13/fs/namei.c -@@ -317,6 +317,29 @@ void path_release_on_umount(struct namei - mntput_no_expire(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -331,12 +354,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -429,10 +449,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -507,8 +526,15 @@ static inline int __do_follow_link(struc - touch_atime(path->mnt, dentry); - nd_set_link(nd, NULL); - -- if (path->mnt == nd->mnt) -- mntget(path->mnt); -+ if (path->mnt != nd->mnt) { -+ dput(nd->dentry); -+ if (nd->mnt != path->mnt) -+ mntput(nd->mnt); -+ nd->mnt = path->mnt; -+ nd->dentry = path->dentry; -+ dget(dentry); -+ } -+ mntget(path->mnt); - cookie = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(cookie); - if (!IS_ERR(cookie)) { -@@ -695,12 +721,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.13/fs/autofs/init.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs/init.c -+++ linux-2.6.13/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.13/fs/autofs/inode.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs/inode.c -+++ linux-2.6.13/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -179,6 +190,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -197,6 +209,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.13/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.13.orig/fs/autofs/autofs_i.h -+++ linux-2.6.13/fs/autofs/autofs_i.h -@@ -150,6 +150,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.13/fs/autofs4/init.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs4/init.c -+++ linux-2.6.13/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.13/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.13.orig/fs/autofs/waitq.c -+++ linux-2.6.13/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.13/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.13.orig/include/linux/compat_ioctl.h -+++ linux-2.6.13/include/linux/compat_ioctl.h -@@ -583,8 +583,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.14-v5-update-20081027.patch b/patches/autofs4-2.6.14-v5-update-20081027.patch deleted file mode 100644 index b2b8c42..0000000 --- a/patches/autofs4-2.6.14-v5-update-20081027.patch +++ /dev/null @@ -1,3146 +0,0 @@ -Index: linux-2.6.14/fs/autofs4/root.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/root.c -+++ linux-2.6.14/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static void *autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,146 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- return -ENOENT; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -- } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -328,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -359,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -430,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -504,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -527,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -537,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -573,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -586,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -598,7 +697,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +714,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -616,13 +727,21 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -636,6 +755,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -645,11 +765,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +788,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -697,51 +831,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -798,11 +894,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.14/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.14/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,38 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t notified; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -129,18 +140,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -154,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -165,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -175,13 +185,6 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); -@@ -199,12 +202,22 @@ static inline int autofs4_follow_mount(s - return res; - } - -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -216,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.14/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/expire.c -+++ linux-2.6.14/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -56,95 +56,163 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } -+ -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -- -- /* Is someone visiting anywhere in the tree ? */ -- if (may_umount_tree(mnt)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -152,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; -+ -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -225,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -241,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -249,66 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -- -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -318,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -334,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -351,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.14/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/inode.c -+++ linux-2.6.14/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -41,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -66,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -85,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -116,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_child.next; -@@ -129,38 +148,66 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ -- if (sbi) -- autofs4_force_release(sbi); -+ autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -169,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -227,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -245,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -253,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -264,16 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -287,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -331,13 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -358,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.14/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/waitq.c -+++ linux-2.6.14/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,51 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -- } -- -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -- kfree(name); -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) - up(&sbi->wq_sem); -- return -ENOENT; -- } -+ kfree(qstr.name); -+ return ret; -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -212,44 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -- atomic_set(&wq->notified, 1); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); -- } else { -- atomic_inc(&wq->wait_ctr); -- up(&sbi->wq_sem); -- kfree(name); -- DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } - -- if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { -- int type = (notify == NFY_MOUNT ? -- autofs_ptype_missing : autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; -+ } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } else { -+ wq->wait_ctr++; -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -- } -- -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -260,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -273,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -285,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.14/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs/dirhash.c -+++ linux-2.6.14/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.14/fs/namespace.c -=================================================================== ---- linux-2.6.14.orig/fs/namespace.c -+++ linux-2.6.14/fs/namespace.c -@@ -308,9 +308,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -330,9 +330,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.14/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.14.orig/include/linux/auto_fs4.h -+++ linux-2.6.14/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 7 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.14/fs/namei.c -=================================================================== ---- linux-2.6.14.orig/fs/namei.c -+++ linux-2.6.14/fs/namei.c -@@ -317,6 +317,29 @@ void path_release_on_umount(struct namei - mntput_no_expire(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -331,12 +354,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -429,10 +449,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -498,6 +517,22 @@ struct path { - struct dentry *dentry; - }; - -+static inline void dput_path(struct path *path, struct nameidata *nd) -+{ -+ dput(path->dentry); -+ if (path->mnt != nd->mnt) -+ mntput(path->mnt); -+} -+ -+static inline void path_to_nameidata(struct path *path, struct nameidata *nd) -+{ -+ dput(nd->dentry); -+ if (nd->mnt != path->mnt) -+ mntput(nd->mnt); -+ nd->mnt = path->mnt; -+ nd->dentry = path->dentry; -+} -+ - static inline int __do_follow_link(struct path *path, struct nameidata *nd) - { - int error; -@@ -507,8 +542,11 @@ static inline int __do_follow_link(struc - touch_atime(path->mnt, dentry); - nd_set_link(nd, NULL); - -- if (path->mnt == nd->mnt) -- mntget(path->mnt); -+ if (path->mnt != nd->mnt) { -+ path_to_nameidata(path, nd); -+ dget(dentry); -+ } -+ mntget(path->mnt); - cookie = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(cookie); - if (!IS_ERR(cookie)) { -@@ -525,22 +563,6 @@ static inline int __do_follow_link(struc - return error; - } - --static inline void dput_path(struct path *path, struct nameidata *nd) --{ -- dput(path->dentry); -- if (path->mnt != nd->mnt) -- mntput(path->mnt); --} -- --static inline void path_to_nameidata(struct path *path, struct nameidata *nd) --{ -- dput(nd->dentry); -- if (nd->mnt != path->mnt) -- mntput(nd->mnt); -- nd->mnt = path->mnt; -- nd->dentry = path->dentry; --} -- - /* - * This limits recursive symlink follows to 8, while - * limiting consecutive symlinks to 40. -@@ -709,12 +731,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.14/fs/autofs/init.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs/init.c -+++ linux-2.6.14/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.14/fs/autofs/inode.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs/inode.c -+++ linux-2.6.14/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -198,6 +210,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.14/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.14.orig/fs/autofs/autofs_i.h -+++ linux-2.6.14/fs/autofs/autofs_i.h -@@ -151,6 +151,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.14/fs/autofs4/init.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs4/init.c -+++ linux-2.6.14/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.14/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.14.orig/fs/autofs/waitq.c -+++ linux-2.6.14/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.14/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.14.orig/include/linux/compat_ioctl.h -+++ linux-2.6.14/include/linux/compat_ioctl.h -@@ -583,8 +583,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.15-v5-update-20081027.patch b/patches/autofs4-2.6.15-v5-update-20081027.patch deleted file mode 100644 index 13b4fde..0000000 --- a/patches/autofs4-2.6.15-v5-update-20081027.patch +++ /dev/null @@ -1,3157 +0,0 @@ -Index: linux-2.6.15/fs/autofs4/root.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/root.c -+++ linux-2.6.15/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -25,28 +25,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static void *autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -54,6 +54,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -62,113 +70,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -176,146 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- return -ENOENT; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -- } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -328,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -359,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -430,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,48 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -504,19 +552,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -527,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -537,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -573,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -586,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -598,7 +697,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +714,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -616,13 +727,21 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -636,6 +755,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -645,11 +765,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +788,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -697,51 +831,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -798,11 +894,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.15/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.15/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,38 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t notified; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -129,18 +140,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -154,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -165,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -175,13 +185,6 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); -@@ -199,12 +202,22 @@ static inline int autofs4_follow_mount(s - return res; - } - -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -216,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.15/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/expire.c -+++ linux-2.6.15/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -56,95 +56,163 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } -+ -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -- -- /* Is someone visiting anywhere in the tree ? */ -- if (may_umount_tree(mnt)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -152,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; -+ -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -225,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -241,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -249,66 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -- -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -318,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -334,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -351,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.15/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/inode.c -+++ linux-2.6.15/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -22,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -39,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -64,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -83,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -114,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_child.next; -@@ -127,38 +148,66 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ -- if (sbi) -- autofs4_force_release(sbi); -+ autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -167,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -225,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -243,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -251,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -262,16 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -285,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -329,13 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -356,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.15/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/waitq.c -+++ linux-2.6.15/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,51 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -- } -- -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -- kfree(name); -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) - up(&sbi->wq_sem); -- return -ENOENT; -- } -+ kfree(qstr.name); -+ return ret; -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -212,42 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -- atomic_set(&wq->notified, 1); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); -- } else { -- atomic_inc(&wq->wait_ctr); -- up(&sbi->wq_sem); -- kfree(name); -- DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } - -- if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { -- int type = (notify == NFY_MOUNT ? -- autofs_ptype_missing : autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; -+ } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } else { -+ wq->wait_ctr++; -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -258,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -271,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -283,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.15/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs/dirhash.c -+++ linux-2.6.15/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.15/fs/namespace.c -=================================================================== ---- linux-2.6.15.orig/fs/namespace.c -+++ linux-2.6.15/fs/namespace.c -@@ -416,9 +416,9 @@ int may_umount_tree(struct vfsmount *mnt - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -438,10 +438,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -- int ret = 0; -+ int ret = 1; - spin_lock(&vfsmount_lock); - if (propagate_mount_busy(mnt, 2)) -- ret = -EBUSY; -+ ret = 0; - spin_unlock(&vfsmount_lock); - return ret; - } -Index: linux-2.6.15/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.15.orig/include/linux/auto_fs4.h -+++ linux-2.6.15/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 7 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.15/fs/namei.c -=================================================================== ---- linux-2.6.15.orig/fs/namei.c -+++ linux-2.6.15/fs/namei.c -@@ -362,6 +362,29 @@ void release_open_intent(struct nameidat - fput(nd->intent.open.file); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -376,12 +399,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -474,10 +494,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -543,6 +562,22 @@ struct path { - struct dentry *dentry; - }; - -+static inline void dput_path(struct path *path, struct nameidata *nd) -+{ -+ dput(path->dentry); -+ if (path->mnt != nd->mnt) -+ mntput(path->mnt); -+} -+ -+static inline void path_to_nameidata(struct path *path, struct nameidata *nd) -+{ -+ dput(nd->dentry); -+ if (nd->mnt != path->mnt) -+ mntput(nd->mnt); -+ nd->mnt = path->mnt; -+ nd->dentry = path->dentry; -+} -+ - static inline int __do_follow_link(struct path *path, struct nameidata *nd) - { - int error; -@@ -552,8 +587,11 @@ static inline int __do_follow_link(struc - touch_atime(path->mnt, dentry); - nd_set_link(nd, NULL); - -- if (path->mnt == nd->mnt) -- mntget(path->mnt); -+ if (path->mnt != nd->mnt) { -+ path_to_nameidata(path, nd); -+ dget(dentry); -+ } -+ mntget(path->mnt); - cookie = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(cookie); - if (!IS_ERR(cookie)) { -@@ -570,22 +608,6 @@ static inline int __do_follow_link(struc - return error; - } - --static inline void dput_path(struct path *path, struct nameidata *nd) --{ -- dput(path->dentry); -- if (path->mnt != nd->mnt) -- mntput(path->mnt); --} -- --static inline void path_to_nameidata(struct path *path, struct nameidata *nd) --{ -- dput(nd->dentry); -- if (nd->mnt != path->mnt) -- mntput(nd->mnt); -- nd->mnt = path->mnt; -- nd->dentry = path->dentry; --} -- - /* - * This limits recursive symlink follows to 8, while - * limiting consecutive symlinks to 40. -@@ -754,12 +776,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.15/fs/autofs/init.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs/init.c -+++ linux-2.6.15/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.15/fs/autofs/inode.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs/inode.c -+++ linux-2.6.15/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -198,6 +210,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.15/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.15.orig/fs/autofs/autofs_i.h -+++ linux-2.6.15/fs/autofs/autofs_i.h -@@ -151,6 +151,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.15/fs/autofs4/init.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs4/init.c -+++ linux-2.6.15/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.15/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.15.orig/fs/autofs/waitq.c -+++ linux-2.6.15/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.15/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.15.orig/include/linux/compat_ioctl.h -+++ linux-2.6.15/include/linux/compat_ioctl.h -@@ -592,8 +592,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.16-v5-update-20081027.patch b/patches/autofs4-2.6.16-v5-update-20081027.patch deleted file mode 100644 index f301545..0000000 --- a/patches/autofs4-2.6.16-v5-update-20081027.patch +++ /dev/null @@ -1,3170 +0,0 @@ -Index: linux-2.6.16/fs/autofs4/root.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/root.c -+++ linux-2.6.16/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -26,28 +26,28 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static void *autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -+struct inode_operations autofs4_indirect_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, -@@ -55,6 +55,14 @@ struct inode_operations autofs4_root_ino - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, -@@ -63,114 +71,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct vfsmount *mnt, struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- touch_atime(mnt, dentry); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, -- struct dentry, d_u.d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -178,176 +82,59 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.dentry = dentry; -- nd.mnt = mnt; -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- return -ENOENT; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -- } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int flags) --{ -- struct super_block *sb = mnt->mnt_sb; -- struct autofs_sb_info *sbi = autofs4_sbi(sb); -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -- /* Wait for a pending mount, triggering one if there isn't one already */ -+ /* -+ * Wait for a pending mount, triggering one if there -+ * isn't one already -+ */ - if (dentry->d_inode == NULL) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - - status = autofs4_wait(sbi, dentry, NFY_MOUNT); -- -+ - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -363,19 +150,96 @@ static int try_to_fill_dentry(struct vfs - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(mnt, dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -384,47 +248,76 @@ static int try_to_fill_dentry(struct vfs - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(nd->mnt, dentry, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(nd->mnt, dentry, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(nd->mnt, dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -434,6 +327,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -453,48 +357,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -508,19 +556,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -531,6 +607,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -541,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -564,8 +652,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -577,9 +670,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -590,11 +683,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -602,7 +701,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -611,7 +718,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -620,13 +731,21 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -640,6 +759,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -649,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -662,6 +792,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -701,51 +835,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -802,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.16/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.16/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,38 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t notified; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -129,18 +140,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -154,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -165,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -175,13 +185,6 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); -@@ -199,12 +202,22 @@ static inline int autofs4_follow_mount(s - return res; - } - -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -216,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.16/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/expire.c -+++ linux-2.6.16/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -56,95 +56,163 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } -+ -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_u.d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_u.d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -- -- /* Is someone visiting anywhere in the tree ? */ -- if (may_umount_tree(mnt)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_u.d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -152,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; -+ -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; - -- if (this_parent != parent) { -- next = this_parent->d_u.d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -225,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -241,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -249,66 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -- -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -318,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -334,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -351,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.16/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/inode.c -+++ linux-2.6.16/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -22,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -39,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -64,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -83,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -114,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_u.d_child.next; -@@ -127,38 +148,66 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ -- if (sbi) -- autofs4_force_release(sbi); -+ autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -167,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -225,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -243,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -251,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -262,16 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -285,38 +357,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -329,13 +409,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -356,8 +431,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.16/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/waitq.c -+++ linux-2.6.16/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,51 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -- } -- -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -- kfree(name); -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) - up(&sbi->wq_sem); -- return -ENOENT; -- } -+ kfree(qstr.name); -+ return ret; -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -212,42 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -- atomic_set(&wq->notified, 1); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); -- } else { -- atomic_inc(&wq->wait_ctr); -- up(&sbi->wq_sem); -- kfree(name); -- DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } - -- if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { -- int type = (notify == NFY_MOUNT ? -- autofs_ptype_missing : autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; -+ } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } else { -+ wq->wait_ctr++; -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -258,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -271,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -283,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.16/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs/dirhash.c -+++ linux-2.6.16/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.16/fs/namespace.c -=================================================================== ---- linux-2.6.16.orig/fs/namespace.c -+++ linux-2.6.16/fs/namespace.c -@@ -421,9 +421,9 @@ int may_umount_tree(struct vfsmount *mnt - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -443,10 +443,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -- int ret = 0; -+ int ret = 1; - spin_lock(&vfsmount_lock); - if (propagate_mount_busy(mnt, 2)) -- ret = -EBUSY; -+ ret = 0; - spin_unlock(&vfsmount_lock); - return ret; - } -Index: linux-2.6.16/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.16.orig/include/linux/auto_fs4.h -+++ linux-2.6.16/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 7 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.16/fs/namei.c -=================================================================== ---- linux-2.6.16.orig/fs/namei.c -+++ linux-2.6.16/fs/namei.c -@@ -365,6 +365,29 @@ void release_open_intent(struct nameidat - fput(nd->intent.open.file); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -379,12 +402,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -477,10 +497,9 @@ static struct dentry * real_lookup(struc - */ - mutex_unlock(&dir->i_mutex); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -546,6 +565,22 @@ struct path { - struct dentry *dentry; - }; - -+static inline void dput_path(struct path *path, struct nameidata *nd) -+{ -+ dput(path->dentry); -+ if (path->mnt != nd->mnt) -+ mntput(path->mnt); -+} -+ -+static inline void path_to_nameidata(struct path *path, struct nameidata *nd) -+{ -+ dput(nd->dentry); -+ if (nd->mnt != path->mnt) -+ mntput(nd->mnt); -+ nd->mnt = path->mnt; -+ nd->dentry = path->dentry; -+} -+ - static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) - { - int error; -@@ -555,8 +590,11 @@ static __always_inline int __do_follow_l - touch_atime(path->mnt, dentry); - nd_set_link(nd, NULL); - -- if (path->mnt == nd->mnt) -- mntget(path->mnt); -+ if (path->mnt != nd->mnt) { -+ path_to_nameidata(path, nd); -+ dget(dentry); -+ } -+ mntget(path->mnt); - cookie = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(cookie); - if (!IS_ERR(cookie)) { -@@ -573,22 +611,6 @@ static __always_inline int __do_follow_l - return error; - } - --static inline void dput_path(struct path *path, struct nameidata *nd) --{ -- dput(path->dentry); -- if (path->mnt != nd->mnt) -- mntput(path->mnt); --} -- --static inline void path_to_nameidata(struct path *path, struct nameidata *nd) --{ -- dput(nd->dentry); -- if (nd->mnt != path->mnt) -- mntput(nd->mnt); -- nd->mnt = path->mnt; -- nd->dentry = path->dentry; --} -- - /* - * This limits recursive symlink follows to 8, while - * limiting consecutive symlinks to 40. -@@ -757,12 +779,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.16/fs/autofs/init.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs/init.c -+++ linux-2.6.16/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.16/fs/autofs/inode.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs/inode.c -+++ linux-2.6.16/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -198,6 +210,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.16/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.16.orig/fs/autofs/autofs_i.h -+++ linux-2.6.16/fs/autofs/autofs_i.h -@@ -151,6 +151,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.16/fs/autofs4/init.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs4/init.c -+++ linux-2.6.16/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.16/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.16.orig/fs/autofs/waitq.c -+++ linux-2.6.16/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.16/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.16.orig/include/linux/compat_ioctl.h -+++ linux-2.6.16/include/linux/compat_ioctl.h -@@ -558,8 +558,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.17-v5-update-20081027.patch b/patches/autofs4-2.6.17-v5-update-20081027.patch deleted file mode 100644 index ea3a4a0..0000000 --- a/patches/autofs4-2.6.17-v5-update-20081027.patch +++ /dev/null @@ -1,2201 +0,0 @@ -Index: linux-2.6.17/fs/autofs4/root.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/root.c -+++ linux-2.6.17/fs/autofs4/root.c -@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -114,155 +82,30 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!ret) { -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -- } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -ENOENT; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -279,9 +122,6 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return status; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { - spin_lock(&dentry->d_lock); -@@ -293,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -320,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -335,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -400,14 +254,36 @@ static int autofs4_revalidate(struct den - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 0; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, flags); -- return !status; -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ -+ return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -421,9 +297,20 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, flags); -- return !status; -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ -+ return status; - } - spin_unlock(&dcache_lock); - -@@ -440,6 +327,17 @@ void autofs4_dentry_release(struct dentr - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -459,10 +357,116 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -478,30 +482,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -515,19 +556,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -549,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -578,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -589,9 +670,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -620,7 +701,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -631,6 +720,9 @@ static int autofs4_dir_rmdir(struct inod - struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -639,6 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -673,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -729,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -830,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.17/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/expire.c -+++ linux-2.6.17/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -174,6 +185,12 @@ static int autofs4_tree_busy(struct vfsm - struct autofs_info *ino = autofs4_dentry_ino(p); - unsigned int ino_count = atomic_read(&ino->count); - -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ - /* allow for dget above and top is already dgot */ - if (p == top) - ino_count += 2; -@@ -253,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -286,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -310,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -320,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -327,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -337,47 +366,81 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -387,7 +450,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -403,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -420,7 +491,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -430,9 +501,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.17/fs/namei.c -=================================================================== ---- linux-2.6.17.orig/fs/namei.c -+++ linux-2.6.17/fs/namei.c -@@ -365,6 +365,29 @@ void release_open_intent(struct nameidat - fput(nd->intent.open.file); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -379,12 +402,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -477,10 +497,9 @@ static struct dentry * real_lookup(struc - */ - mutex_unlock(&dir->i_mutex); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -760,12 +779,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.17/fs/autofs/init.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs/init.c -+++ linux-2.6.17/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.17/fs/autofs/inode.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs/inode.c -+++ linux-2.6.17/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -198,6 +210,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.17/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.17.orig/fs/autofs/autofs_i.h -+++ linux-2.6.17/fs/autofs/autofs_i.h -@@ -151,6 +151,7 @@ extern const struct file_operations auto - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.17/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.17/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -54,6 +56,11 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -68,15 +75,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,18 +91,13 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; -- struct dentry *root; - int pipefd; - struct file *pipe; - pid_t oz_pgrp; -@@ -113,6 +114,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -137,18 +141,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -162,6 +162,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -231,4 +232,4 @@ out: - } - - void autofs4_dentry_release(struct dentry *); -- -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.17/fs/autofs4/init.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/init.c -+++ linux-2.6.17/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.17/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/inode.c -+++ linux-2.6.17/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,14 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -95,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -126,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_u.d_child.next; -@@ -139,29 +148,34 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); - } - - static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -@@ -188,7 +202,6 @@ static int autofs4_show_options(struct s - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, - .show_options = autofs4_show_options, - }; -@@ -272,7 +285,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -314,20 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; - sbi->pipefd = -1; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -362,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -@@ -394,13 +410,7 @@ int autofs4_fill_super(struct super_bloc - goto fail_fput; - sbi->pipe = pipe; - sbi->pipefd = pipefd; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -425,6 +435,7 @@ fail_ino: - kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.17/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs4/waitq.c -+++ linux-2.6.17/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,15 +42,17 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ mutex_unlock(&sbi->wq_mutex); - shrink_dcache_sb(sbi->sb); - } - -@@ -87,11 +95,16 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -101,26 +114,26 @@ static void autofs4_notify_daemon(struct - /* Kernel protocol v4 missing and expire packets */ - case autofs_ptype_missing: - { -- struct autofs_packet_missing *mp = &pkt.missing; -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: - { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -132,14 +145,14 @@ static void autofs4_notify_daemon(struct - case autofs_ptype_missing_direct: - case autofs_ptype_expire_direct: - { -- struct autofs_v5_packet *packet = &pkt.v5_packet; -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; - - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -153,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -170,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -190,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -252,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -266,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -288,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -298,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -308,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } -- -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -350,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -363,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -386,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.17/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.17.orig/fs/autofs/waitq.c -+++ linux-2.6.17/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.17/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.17.orig/include/linux/auto_fs4.h -+++ linux-2.6.17/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -59,6 +64,13 @@ struct autofs_packet_expire_multi { - char name[NAME_MAX+1]; - }; - -+union autofs_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_packet_missing missing; -+ struct autofs_packet_expire expire; -+ struct autofs_packet_expire_multi expire_multi; -+}; -+ - /* autofs v5 common packet struct */ - struct autofs_v5_packet { - struct autofs_packet_hdr hdr; -@@ -78,20 +90,19 @@ typedef struct autofs_v5_packet autofs_p - typedef struct autofs_v5_packet autofs_packet_missing_direct_t; - typedef struct autofs_v5_packet autofs_packet_expire_direct_t; - --union autofs_packet_union { -+union autofs_v5_packet_union { - struct autofs_packet_hdr hdr; -- struct autofs_packet_missing missing; -- struct autofs_packet_expire expire; -- struct autofs_packet_expire_multi expire_multi; - struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; - }; - - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.17/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.17.orig/include/linux/compat_ioctl.h -+++ linux-2.6.17/include/linux/compat_ioctl.h -@@ -564,8 +564,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) diff --git a/patches/autofs4-2.6.18-v5-update-20081027.patch b/patches/autofs4-2.6.18-v5-update-20081027.patch deleted file mode 100644 index 9c39b30..0000000 --- a/patches/autofs4-2.6.18-v5-update-20081027.patch +++ /dev/null @@ -1,2186 +0,0 @@ -Index: linux-2.6.18/fs/autofs4/root.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/root.c -+++ linux-2.6.18/fs/autofs4/root.c -@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -114,155 +82,30 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!ret) { -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -- } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -ENOENT; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -279,9 +122,6 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return status; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { - spin_lock(&dentry->d_lock); -@@ -293,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -320,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -335,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -400,14 +254,36 @@ static int autofs4_revalidate(struct den - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 0; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, flags); -- return !status; -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ -+ return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -421,9 +297,20 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, flags); -- return !status; -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ -+ return status; - } - spin_unlock(&dcache_lock); - -@@ -440,6 +327,17 @@ void autofs4_dentry_release(struct dentr - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -459,10 +357,116 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -478,30 +482,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -515,19 +556,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -549,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -578,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -589,9 +670,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -620,7 +701,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -631,6 +720,9 @@ static int autofs4_dir_rmdir(struct inod - struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -639,6 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -673,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -729,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -830,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.18/fs/namei.c -=================================================================== ---- linux-2.6.18.orig/fs/namei.c -+++ linux-2.6.18/fs/namei.c -@@ -372,6 +372,29 @@ void release_open_intent(struct nameidat - fput(nd->intent.open.file); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -386,12 +409,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -484,10 +504,9 @@ static struct dentry * real_lookup(struc - */ - mutex_unlock(&dir->i_mutex); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -767,12 +786,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -Index: linux-2.6.18/fs/autofs/init.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs/init.c -+++ linux-2.6.18/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.18/fs/autofs/inode.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs/inode.c -+++ linux-2.6.18/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -198,6 +210,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.18/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.18.orig/fs/autofs/autofs_i.h -+++ linux-2.6.18/fs/autofs/autofs_i.h -@@ -151,6 +151,7 @@ extern const struct file_operations auto - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.18/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.18/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -54,6 +56,11 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -68,15 +75,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,18 +91,13 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; -- struct dentry *root; - int pipefd; - struct file *pipe; - pid_t oz_pgrp; -@@ -113,6 +114,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -137,18 +141,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -162,6 +162,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -231,4 +232,4 @@ out: - } - - void autofs4_dentry_release(struct dentry *); -- -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.18/fs/autofs4/init.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/init.c -+++ linux-2.6.18/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.18/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/inode.c -+++ linux-2.6.18/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,14 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -95,9 +101,12 @@ void autofs4_free_ino(struct autofs_info - */ - static void autofs4_force_release(struct autofs_sb_info *sbi) - { -- struct dentry *this_parent = sbi->root; -+ struct dentry *this_parent = sbi->sb->s_root; - struct list_head *next; - -+ if (!sbi->sb->s_root) -+ return; -+ - spin_lock(&dcache_lock); - repeat: - next = this_parent->d_subdirs.next; -@@ -126,7 +135,7 @@ resume: - spin_lock(&dcache_lock); - } - -- if (this_parent != sbi->root) { -+ if (this_parent != sbi->sb->s_root) { - struct dentry *dentry = this_parent; - - next = this_parent->d_u.d_child.next; -@@ -139,29 +148,34 @@ resume: - goto resume; - } - spin_unlock(&dcache_lock); -- -- dput(sbi->root); -- sbi->root = NULL; - shrink_dcache_sb(sbi->sb); -- -- return; - } - --static void autofs4_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); - } - - static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -@@ -188,7 +202,6 @@ static int autofs4_show_options(struct s - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, - .show_options = autofs4_show_options, - }; -@@ -272,7 +285,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -314,20 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->root = NULL; - sbi->pipefd = -1; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -362,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -@@ -394,13 +410,7 @@ int autofs4_fill_super(struct super_bloc - goto fail_fput; - sbi->pipe = pipe; - sbi->pipefd = pipefd; -- -- /* -- * Take a reference to the root dentry so we get a chance to -- * clean up the dentry tree on umount. -- * See autofs4_force_release. -- */ -- sbi->root = dget(root); -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -425,6 +435,7 @@ fail_ino: - kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.18/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/waitq.c -+++ linux-2.6.18/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,15 +42,17 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ mutex_unlock(&sbi->wq_mutex); - shrink_dcache_sb(sbi->sb); - } - -@@ -87,11 +95,16 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -101,26 +114,26 @@ static void autofs4_notify_daemon(struct - /* Kernel protocol v4 missing and expire packets */ - case autofs_ptype_missing: - { -- struct autofs_packet_missing *mp = &pkt.missing; -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: - { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -132,14 +145,14 @@ static void autofs4_notify_daemon(struct - case autofs_ptype_missing_direct: - case autofs_ptype_expire_direct: - { -- struct autofs_v5_packet *packet = &pkt.v5_packet; -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; - - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -153,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -170,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -190,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -252,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -266,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -288,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -298,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -308,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } -- -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -350,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -363,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -386,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.18/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs/waitq.c -+++ linux-2.6.18/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.18/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.18.orig/include/linux/auto_fs4.h -+++ linux-2.6.18/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -59,6 +64,13 @@ struct autofs_packet_expire_multi { - char name[NAME_MAX+1]; - }; - -+union autofs_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_packet_missing missing; -+ struct autofs_packet_expire expire; -+ struct autofs_packet_expire_multi expire_multi; -+}; -+ - /* autofs v5 common packet struct */ - struct autofs_v5_packet { - struct autofs_packet_hdr hdr; -@@ -78,20 +90,19 @@ typedef struct autofs_v5_packet autofs_p - typedef struct autofs_v5_packet autofs_packet_missing_direct_t; - typedef struct autofs_v5_packet autofs_packet_expire_direct_t; - --union autofs_packet_union { -+union autofs_v5_packet_union { - struct autofs_packet_hdr hdr; -- struct autofs_packet_missing missing; -- struct autofs_packet_expire expire; -- struct autofs_packet_expire_multi expire_multi; - struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; - }; - - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.18/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.18.orig/fs/autofs4/expire.c -+++ linux-2.6.18/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.18/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.18.orig/include/linux/compat_ioctl.h -+++ linux-2.6.18/include/linux/compat_ioctl.h -@@ -565,8 +565,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) diff --git a/patches/autofs4-2.6.18-v5-update-20090903.patch b/patches/autofs4-2.6.18-v5-update-20090903.patch new file mode 100644 index 0000000..1fd315a --- /dev/null +++ b/patches/autofs4-2.6.18-v5-update-20090903.patch @@ -0,0 +1,3928 @@ +--- linux-2.6.18.orig/fs/autofs4/root.c ++++ linux-2.6.18/fs/autofs4/root.c +@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_dentry; +- struct vfsmount *mnt = file->f_vfsmnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -114,155 +82,30 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (!ret) { +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -ENOENT; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -279,9 +122,6 @@ static int try_to_fill_dentry(struct den + + DPRINTK("mount done status=%d", status); + +- if (status && dentry->d_inode) +- return status; /* Try to get the kernel to invalidate this dentry */ +- + /* Turn this into a real negative dentry? */ + if (status == -ENOENT) { + spin_lock(&dentry->d_lock); +@@ -293,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -320,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -335,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -400,14 +254,36 @@ static int autofs4_revalidate(struct den + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + int oz_mode = autofs4_oz_mode(sbi); + int flags = nd ? nd->flags : 0; +- int status = 0; ++ int status; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { +- if (!oz_mode) +- status = try_to_fill_dentry(dentry, flags); +- return !status; ++ /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ ++ if (oz_mode) ++ return 1; ++ ++ /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* ++ * A zero status is success otherwise we have a ++ * negative error code. ++ */ ++ status = try_to_fill_dentry(dentry, flags); ++ if (status == 0) ++ return 1; ++ ++ return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -421,9 +297,20 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); +- if (!oz_mode) +- status = try_to_fill_dentry(dentry, flags); +- return !status; ++ ++ /* The daemon never causes a mount to trigger */ ++ if (oz_mode) ++ return 1; ++ ++ /* ++ * A zero status is success otherwise we have a ++ * negative error code. ++ */ ++ status = try_to_fill_dentry(dentry, flags); ++ if (status == 0) ++ return 1; ++ ++ return status; + } + spin_unlock(&dcache_lock); + +@@ -440,6 +327,17 @@ void autofs4_dentry_release(struct dentr + de->d_fsdata = NULL; + + if (inf) { ++ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); ++ ++ if (sbi) { ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ } ++ + inf->dentry = NULL; + inf->inode = NULL; + +@@ -459,10 +357,116 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Bad luck, we've already been dentry_iput */ ++ if (!dentry->d_inode) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ + /* Lookups in the root directory */ + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -478,29 +482,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. +- */ +- dentry->d_op = &autofs4_root_dentry_operations; ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { ++ /* ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. ++ */ ++ dentry->d_op = &autofs4_root_dentry_operations; ++ ++ /* ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. ++ */ ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); ++ } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -515,19 +557,47 @@ static struct dentry *autofs4_lookup(str + if (sigismember (sigset, SIGKILL) || + sigismember (sigset, SIGQUIT) || + sigismember (sigset, SIGINT)) { ++ if (unhashed) ++ dput(unhashed); + return ERR_PTR(-ERESTARTNOINTR); + } + } ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* + * If this dentry is unhashed, then we shouldn't honour this +- * lookup even if the dentry is positive. Returning ENOENT here +- * doesn't do the right thing for all system calls, but it should +- * be OK for the operations we permit from an autofs. ++ * lookup. Returning ENOENT here doesn't do the right thing ++ * for all system calls, but it should be OK for the operations ++ * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) +- return ERR_PTR(-ENOENT); ++ if (!oz_mode && d_unhashed(dentry)) { ++ /* ++ * A user space application can (and has done in the past) ++ * remove and re-create this directory during the callback. ++ * This can leave us with an unhashed dentry, but a ++ * successful mount! So we need to perform another ++ * cached lookup in case the dentry now exists. ++ */ ++ struct dentry *parent = dentry->d_parent; ++ struct dentry *new = d_lookup(parent, &dentry->d_name); ++ if (new != NULL) ++ dentry = new; ++ else ++ dentry = ERR_PTR(-ENOENT); ++ ++ if (unhashed) ++ dput(unhashed); ++ ++ return dentry; ++ } ++ ++ if (unhashed) ++ return unhashed; + + return NULL; + } +@@ -549,21 +619,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -578,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -589,9 +671,9 @@ static int autofs4_dir_symlink(struct in + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want +- * this, because since the unlink is probably the result of an expire. +- * We simply d_drop it, which allows the dentry lookup to remount it +- * if necessary. ++ * this, because the unlink is probably the result of an expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -620,7 +702,15 @@ static int autofs4_dir_unlink(struct ino + + dir->i_mtime = CURRENT_TIME; + +- d_drop(dentry); ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); ++ spin_lock(&dentry->d_lock); ++ __d_drop(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&dcache_lock); + + return 0; + } +@@ -631,6 +721,9 @@ static int autofs4_dir_rmdir(struct inod + struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; + ++ DPRINTK("dentry %p, removing %.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ + if (!autofs4_oz_mode(sbi)) + return -EACCES; + +@@ -639,6 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -673,11 +770,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -729,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if ( status ) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -830,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_vfsmnt, p); + +--- linux-2.6.18.orig/fs/namei.c ++++ linux-2.6.18/fs/namei.c +@@ -372,6 +372,29 @@ void release_open_intent(struct nameidat + fput(nd->intent.open.file); + } + ++static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ int status = dentry->d_op->d_revalidate(dentry, nd); ++ if (unlikely(status <= 0)) { ++ /* ++ * The dentry failed validation. ++ * If d_revalidate returned 0 attempt to invalidate ++ * the dentry otherwise d_revalidate is asking us ++ * to return a fail status. ++ */ ++ if (!status) { ++ if (!d_invalidate(dentry)) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ } else { ++ dput(dentry); ++ dentry = ERR_PTR(status); ++ } ++ } ++ return dentry; ++} ++ + /* + * Internal lookup() using the new generic dcache. + * SMP-safe +@@ -386,12 +409,9 @@ static struct dentry * cached_lookup(str + if (!dentry) + dentry = d_lookup(parent, name); + +- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { +- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { +- dput(dentry); +- dentry = NULL; +- } +- } ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) ++ dentry = do_revalidate(dentry, nd); ++ + return dentry; + } + +@@ -484,10 +504,9 @@ static struct dentry * real_lookup(struc + */ + mutex_unlock(&dir->i_mutex); + if (result->d_op && result->d_op->d_revalidate) { +- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { +- dput(result); ++ result = do_revalidate(result, nd); ++ if (!result) + result = ERR_PTR(-ENOENT); +- } + } + return result; + } +@@ -767,12 +786,12 @@ need_lookup: + goto done; + + need_revalidate: +- if (dentry->d_op->d_revalidate(dentry, nd)) +- goto done; +- if (d_invalidate(dentry)) +- goto done; +- dput(dentry); +- goto need_lookup; ++ dentry = do_revalidate(dentry, nd); ++ if (!dentry) ++ goto need_lookup; ++ if (IS_ERR(dentry)) ++ goto fail; ++ goto done; + + fail: + return PTR_ERR(dentry); +--- linux-2.6.18.orig/fs/autofs/init.c ++++ linux-2.6.18/fs/autofs/init.c +@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs + .owner = THIS_MODULE, + .name = "autofs", + .get_sb = autofs_get_sb, +- .kill_sb = kill_anon_super, ++ .kill_sb = autofs_kill_sb, + }; + + static int __init init_autofs_fs(void) +--- linux-2.6.18.orig/fs/autofs/inode.c ++++ linux-2.6.18/fs/autofs/inode.c +@@ -19,11 +19,20 @@ + #include "autofs_i.h" + #include + +-static void autofs_put_super(struct super_block *sb) ++void autofs4_kill_sb(struct super_block *sb) + { + struct autofs_sb_info *sbi = autofs_sbi(sb); + unsigned int n; + ++ /* ++ * In the event of a failure in get_sb_nodev the superblock ++ * info is not present so nothing else has been setup, so ++ * just call kill_anon_super when we are called from ++ * deactivate_super. ++ */ ++ if (!sbi) ++ goto out_kill_sb; ++ + if ( !sbi->catatonic ) + autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ + +@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe + + kfree(sb->s_fs_info); + ++out_kill_sb: + DPRINTK(("autofs: shutting down\n")); ++ kill_anon_super(sb); + } + + static void autofs_read_inode(struct inode *inode); + + static struct super_operations autofs_sops = { + .read_inode = autofs_read_inode, +- .put_super = autofs_put_super, + .statfs = simple_statfs, + }; + +@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block + + s->s_fs_info = sbi; + sbi->magic = AUTOFS_SBI_MAGIC; +- sbi->catatonic = 0; ++ sbi->pipe = NULL; ++ sbi->catatonic = 1; + sbi->exp_timeout = 0; + sbi->oz_pgrp = process_group(current); + autofs_initialize_hash(&sbi->dirhash); +@@ -180,6 +191,7 @@ int autofs_fill_super(struct super_block + if ( !pipe->f_op || !pipe->f_op->write ) + goto fail_fput; + sbi->pipe = pipe; ++ sbi->catatonic = 0; + + /* + * Success! Install the root dentry now to indicate completion. +@@ -198,6 +210,7 @@ fail_iput: + iput(root_inode); + fail_free: + kfree(sbi); ++ s->s_fs_info = NULL; + fail_unlock: + return -EINVAL; + } +--- linux-2.6.18.orig/fs/autofs/autofs_i.h ++++ linux-2.6.18/fs/autofs/autofs_i.h +@@ -151,6 +151,7 @@ extern const struct file_operations auto + /* Initializing function */ + + int autofs_fill_super(struct super_block *, void *, int); ++void autofs_kill_sb(struct super_block *); + + /* Queue management functions */ + +--- linux-2.6.18.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.18/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else + #define DPRINTK(fmt,args...) do {} while(0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + #define AUTOFS_SUPER_MAGIC 0x0187 + + /* Unified info structure. This is pointed to by both the dentry and +@@ -54,10 +74,18 @@ struct autofs_info { + + int flags; + ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; ++ + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +96,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,18 +112,13 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; +- struct dentry *root; + int pipefd; + struct file *pipe; + pid_t oz_pgrp; +@@ -113,6 +135,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -137,18 +162,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -162,11 +183,23 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +@@ -231,4 +264,4 @@ out: + } + + void autofs4_dentry_release(struct dentry *); +- ++extern void autofs4_kill_sb(struct super_block *); +--- linux-2.6.18.orig/fs/autofs4/init.c ++++ linux-2.6.18/fs/autofs4/init.c +@@ -24,16 +24,25 @@ static struct file_system_type autofs_fs + .owner = THIS_MODULE, + .name = "autofs", + .get_sb = autofs_get_sb, +- .kill_sb = kill_anon_super, ++ .kill_sb = autofs4_kill_sb, + }; + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- linux-2.6.18.orig/fs/autofs4/inode.c ++++ linux-2.6.18/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,14 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -95,9 +103,12 @@ void autofs4_free_ino(struct autofs_info + */ + static void autofs4_force_release(struct autofs_sb_info *sbi) + { +- struct dentry *this_parent = sbi->root; ++ struct dentry *this_parent = sbi->sb->s_root; + struct list_head *next; + ++ if (!sbi->sb->s_root) ++ return; ++ + spin_lock(&dcache_lock); + repeat: + next = this_parent->d_subdirs.next; +@@ -126,7 +137,7 @@ resume: + spin_lock(&dcache_lock); + } + +- if (this_parent != sbi->root) { ++ if (this_parent != sbi->sb->s_root) { + struct dentry *dentry = this_parent; + + next = this_parent->d_u.d_child.next; +@@ -139,29 +150,34 @@ resume: + goto resume; + } + spin_unlock(&dcache_lock); +- +- dput(sbi->root); +- sbi->root = NULL; + shrink_dcache_sb(sbi->sb); +- +- return; + } + +-static void autofs4_put_super(struct super_block *sb) ++void autofs4_kill_sb(struct super_block *sb) + { + struct autofs_sb_info *sbi = autofs4_sbi(sb); + +- sb->s_fs_info = NULL; ++ /* ++ * In the event of a failure in get_sb_nodev the superblock ++ * info is not present so nothing else has been setup, so ++ * just call kill_anon_super when we are called from ++ * deactivate_super. ++ */ ++ if (!sbi) ++ goto out_kill_sb; + +- if ( !sbi->catatonic ) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); + ++ sb->s_fs_info = NULL; + kfree(sbi); + ++out_kill_sb: + DPRINTK("shutting down"); ++ kill_anon_super(sb); + } + + static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) +@@ -177,9 +193,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -188,7 +204,6 @@ static int autofs4_show_options(struct s + } + + static struct super_operations autofs4_sops = { +- .put_super = autofs4_put_super, + .statfs = simple_statfs, + .show_options = autofs4_show_options, + }; +@@ -266,13 +281,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -314,20 +329,23 @@ int autofs4_fill_super(struct super_bloc + + s->s_fs_info = sbi; + sbi->magic = AUTOFS_SBI_MAGIC; +- sbi->root = NULL; + sbi->pipefd = -1; +- sbi->catatonic = 0; ++ sbi->pipe = NULL; ++ sbi->catatonic = 1; + sbi->exp_timeout = 0; + sbi->oz_pgrp = process_group(current); + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -362,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +@@ -394,13 +412,7 @@ int autofs4_fill_super(struct super_bloc + goto fail_fput; + sbi->pipe = pipe; + sbi->pipefd = pipefd; +- +- /* +- * Take a reference to the root dentry so we get a chance to +- * clean up the dentry tree on umount. +- * See autofs4_force_release. +- */ +- sbi->root = dget(root); ++ sbi->catatonic = 0; + + /* + * Success! Install the root dentry now to indicate completion. +@@ -425,6 +437,7 @@ fail_ino: + kfree(ino); + fail_free: + kfree(sbi); ++ s->s_fs_info = NULL; + fail_unlock: + return -EINVAL; + } +--- linux-2.6.18.orig/fs/autofs4/waitq.c ++++ linux-2.6.18/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,15 +42,17 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } +- if (sbi->pipe) { +- fput(sbi->pipe); /* Close the pipe */ +- sbi->pipe = NULL; +- } ++ fput(sbi->pipe); /* Close the pipe */ ++ sbi->pipe = NULL; ++ mutex_unlock(&sbi->wq_mutex); + shrink_dcache_sb(sbi->sb); + } + +@@ -87,11 +95,16 @@ static void autofs4_notify_daemon(struct + struct autofs_wait_queue *wq, + int type) + { +- union autofs_packet_union pkt; ++ union { ++ struct autofs_packet_hdr hdr; ++ union autofs_packet_union v4_pkt; ++ union autofs_v5_packet_union v5_pkt; ++ } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -101,26 +114,26 @@ static void autofs4_notify_daemon(struct + /* Kernel protocol v4 missing and expire packets */ + case autofs_ptype_missing: + { +- struct autofs_packet_missing *mp = &pkt.missing; ++ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; + + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: + { +- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; ++ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; + + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -132,14 +145,14 @@ static void autofs4_notify_daemon(struct + case autofs_ptype_missing_direct: + case autofs_ptype_expire_direct: + { +- struct autofs_v5_packet *packet = &pkt.v5_packet; ++ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; + + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -153,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -170,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -190,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -252,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -266,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -288,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -298,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -308,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -350,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -362,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -386,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.18.orig/fs/autofs/waitq.c ++++ linux-2.6.18/fs/autofs/waitq.c +@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ ++ sbi->pipe = NULL; + autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ + } + +--- linux-2.6.18.orig/include/linux/auto_fs4.h ++++ linux-2.6.18/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -59,6 +118,13 @@ struct autofs_packet_expire_multi { + char name[NAME_MAX+1]; + }; + ++union autofs_packet_union { ++ struct autofs_packet_hdr hdr; ++ struct autofs_packet_missing missing; ++ struct autofs_packet_expire expire; ++ struct autofs_packet_expire_multi expire_multi; ++}; ++ + /* autofs v5 common packet struct */ + struct autofs_v5_packet { + struct autofs_packet_hdr hdr; +@@ -78,20 +144,19 @@ typedef struct autofs_v5_packet autofs_p + typedef struct autofs_v5_packet autofs_packet_missing_direct_t; + typedef struct autofs_v5_packet autofs_packet_expire_direct_t; + +-union autofs_packet_union { ++union autofs_v5_packet_union { + struct autofs_packet_hdr hdr; +- struct autofs_packet_missing missing; +- struct autofs_packet_expire expire; +- struct autofs_packet_expire_multi expire_multi; + struct autofs_v5_packet v5_packet; ++ autofs_packet_missing_indirect_t missing_indirect; ++ autofs_packet_expire_indirect_t expire_indirect; ++ autofs_packet_missing_direct_t missing_direct; ++ autofs_packet_expire_direct_t expire_direct; + }; + + #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- linux-2.6.18.orig/fs/autofs4/expire.c ++++ linux-2.6.18/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if ( !sbi->exp_timeout || !root ) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,9 +469,15 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + + /* Call repeatedly until it returns -EAGAIN, meaning there's nothing +@@ -425,7 +492,7 @@ int autofs4_expire_multi(struct super_bl + if (arg && get_user(do_now, arg)) + return -EFAULT; + +- if (sbi->type & AUTOFS_TYPE_DIRECT) ++ if (autofs_type_trigger(sbi->type)) + dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); + else + dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); +@@ -435,9 +502,16 @@ int autofs4_expire_multi(struct super_bl + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + +--- linux-2.6.18.orig/include/linux/compat_ioctl.h ++++ linux-2.6.18/include/linux/compat_ioctl.h +@@ -565,8 +565,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- /dev/null ++++ linux-2.6.18/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.18.orig/fs/autofs4/Makefile ++++ linux-2.6.18/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.18/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,867 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct dentry *dentry; ++ struct vfsmount *mnt; ++ int err = -EAGAIN; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_vfsmnt; ++ ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sbi->sb, mnt, sbi, how); ++ else ++ dentry = autofs4_expire_indirect(sbi->sb, mnt, sbi, how); ++ ++ if (dentry) { ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ ++ /* ++ * This is synchronous because it makes the daemon a ++ * little easier ++ */ ++ err = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ sbi->sb->s_root->d_mounted++; ++ } ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ dput(dentry); ++ } ++ ++ return err; ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_vfsmnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t devid = new_encode_dev(sbi->sb->s_dev); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ devid = autofs4_get_dev(sbi); ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- /dev/null ++++ linux-2.6.18/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.18.orig/include/linux/auto_fs.h ++++ linux-2.6.18/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.19-v5-update-20081027.patch b/patches/autofs4-2.6.19-v5-update-20081027.patch deleted file mode 100644 index ef8402b..0000000 --- a/patches/autofs4-2.6.19-v5-update-20081027.patch +++ /dev/null @@ -1,1909 +0,0 @@ -Index: linux-2.6.19/fs/autofs/inode.c -=================================================================== ---- linux-2.6.19.orig/fs/autofs/inode.c -+++ linux-2.6.19/fs/autofs/inode.c -@@ -28,10 +28,11 @@ void autofs_kill_sb(struct super_block * - /* - * In the event of a failure in get_sb_nodev the superblock - * info is not present so nothing else has been setup, so -- * just exit when we are called from deactivate_super. -+ * just call kill_anon_super when we are called from -+ * deactivate_super. - */ - if (!sbi) -- return; -+ goto out_kill_sb; - - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ -@@ -44,6 +45,7 @@ void autofs_kill_sb(struct super_block * - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); - kill_anon_super(sb); - } -@@ -209,7 +211,6 @@ fail_iput: - fail_free: - kfree(sbi); - s->s_fs_info = NULL; -- kill_anon_super(s); - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.19/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.19.orig/fs/autofs4/inode.c -+++ linux-2.6.19/fs/autofs4/inode.c -@@ -25,8 +25,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -42,14 +44,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -152,21 +158,22 @@ void autofs4_kill_sb(struct super_block - /* - * In the event of a failure in get_sb_nodev the superblock - * info is not present so nothing else has been setup, so -- * just exit when we are called from deactivate_super. -+ * just call kill_anon_super when we are called from -+ * deactivate_super. - */ - if (!sbi) -- return; -- -- sb->s_fs_info = NULL; -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); - kill_anon_super(sb); - } -@@ -278,7 +285,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -328,12 +335,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -368,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -@@ -426,7 +436,6 @@ fail_ino: - fail_free: - kfree(sbi); - s->s_fs_info = NULL; -- kill_anon_super(s); - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.19/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.19.orig/fs/autofs4/waitq.c -+++ linux-2.6.19/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -84,11 +95,16 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -98,26 +114,26 @@ static void autofs4_notify_daemon(struct - /* Kernel protocol v4 missing and expire packets */ - case autofs_ptype_missing: - { -- struct autofs_packet_missing *mp = &pkt.missing; -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: - { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -129,14 +145,14 @@ static void autofs4_notify_daemon(struct - case autofs_ptype_missing_direct: - case autofs_ptype_expire_direct: - { -- struct autofs_v5_packet *packet = &pkt.v5_packet; -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; - - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -150,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -167,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -187,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -249,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -263,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -285,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -295,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -305,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -347,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -360,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -383,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.19/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.19.orig/include/linux/auto_fs4.h -+++ linux-2.6.19/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -59,6 +64,13 @@ struct autofs_packet_expire_multi { - char name[NAME_MAX+1]; - }; - -+union autofs_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_packet_missing missing; -+ struct autofs_packet_expire expire; -+ struct autofs_packet_expire_multi expire_multi; -+}; -+ - /* autofs v5 common packet struct */ - struct autofs_v5_packet { - struct autofs_packet_hdr hdr; -@@ -78,20 +90,19 @@ typedef struct autofs_v5_packet autofs_p - typedef struct autofs_v5_packet autofs_packet_missing_direct_t; - typedef struct autofs_v5_packet autofs_packet_expire_direct_t; - --union autofs_packet_union { -+union autofs_v5_packet_union { - struct autofs_packet_hdr hdr; -- struct autofs_packet_missing missing; -- struct autofs_packet_expire expire; -- struct autofs_packet_expire_multi expire_multi; - struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; - }; - - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.19/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.19.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.19/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,6 +54,11 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -66,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -83,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -110,6 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -134,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -159,6 +160,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.19/fs/autofs4/root.c -=================================================================== ---- linux-2.6.19.orig/fs/autofs4/root.c -+++ linux-2.6.19/fs/autofs4/root.c -@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -114,157 +82,30 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -- } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -ENOENT; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -292,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -319,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -334,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -402,21 +257,33 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) -- return 1; -+ return 1; - - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -430,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -459,6 +327,17 @@ void autofs4_dentry_release(struct dentr - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -478,10 +357,116 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -497,30 +482,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -534,22 +556,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -571,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -600,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -611,9 +670,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -642,7 +701,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -653,6 +720,9 @@ static int autofs4_dir_rmdir(struct inod - struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -661,6 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -695,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -751,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -852,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.19/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.19.orig/fs/autofs4/expire.c -+++ linux-2.6.19/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.19/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.19.orig/include/linux/compat_ioctl.h -+++ linux-2.6.19/include/linux/compat_ioctl.h -@@ -568,8 +568,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) diff --git a/patches/autofs4-2.6.19-v5-update-20090903.patch b/patches/autofs4-2.6.19-v5-update-20090903.patch new file mode 100644 index 0000000..87102db --- /dev/null +++ b/patches/autofs4-2.6.19-v5-update-20090903.patch @@ -0,0 +1,3667 @@ +--- linux-2.6.19.orig/fs/autofs/inode.c ++++ linux-2.6.19/fs/autofs/inode.c +@@ -28,10 +28,11 @@ void autofs_kill_sb(struct super_block * + /* + * In the event of a failure in get_sb_nodev the superblock + * info is not present so nothing else has been setup, so +- * just exit when we are called from deactivate_super. ++ * just call kill_anon_super when we are called from ++ * deactivate_super. + */ + if (!sbi) +- return; ++ goto out_kill_sb; + + if ( !sbi->catatonic ) + autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ +@@ -44,6 +45,7 @@ void autofs_kill_sb(struct super_block * + + kfree(sb->s_fs_info); + ++out_kill_sb: + DPRINTK(("autofs: shutting down\n")); + kill_anon_super(sb); + } +@@ -209,7 +211,6 @@ fail_iput: + fail_free: + kfree(sbi); + s->s_fs_info = NULL; +- kill_anon_super(s); + fail_unlock: + return -EINVAL; + } +--- linux-2.6.19.orig/fs/autofs4/inode.c ++++ linux-2.6.19/fs/autofs4/inode.c +@@ -25,8 +25,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -42,14 +44,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -152,21 +160,22 @@ void autofs4_kill_sb(struct super_block + /* + * In the event of a failure in get_sb_nodev the superblock + * info is not present so nothing else has been setup, so +- * just exit when we are called from deactivate_super. ++ * just call kill_anon_super when we are called from ++ * deactivate_super. + */ + if (!sbi) +- return; +- +- sb->s_fs_info = NULL; ++ goto out_kill_sb; + +- if ( !sbi->catatonic ) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); + ++ sb->s_fs_info = NULL; + kfree(sbi); + ++out_kill_sb: + DPRINTK("shutting down"); + kill_anon_super(sb); + } +@@ -184,9 +193,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -272,13 +281,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -328,12 +337,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -368,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +@@ -426,7 +438,6 @@ fail_ino: + fail_free: + kfree(sbi); + s->s_fs_info = NULL; +- kill_anon_super(s); + fail_unlock: + return -EINVAL; + } +--- linux-2.6.19.orig/fs/autofs4/waitq.c ++++ linux-2.6.19/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -84,11 +95,16 @@ static void autofs4_notify_daemon(struct + struct autofs_wait_queue *wq, + int type) + { +- union autofs_packet_union pkt; ++ union { ++ struct autofs_packet_hdr hdr; ++ union autofs_packet_union v4_pkt; ++ union autofs_v5_packet_union v5_pkt; ++ } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -98,26 +114,26 @@ static void autofs4_notify_daemon(struct + /* Kernel protocol v4 missing and expire packets */ + case autofs_ptype_missing: + { +- struct autofs_packet_missing *mp = &pkt.missing; ++ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; + + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: + { +- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; ++ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; + + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -129,14 +145,14 @@ static void autofs4_notify_daemon(struct + case autofs_ptype_missing_direct: + case autofs_ptype_expire_direct: + { +- struct autofs_v5_packet *packet = &pkt.v5_packet; ++ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; + + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -150,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -167,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -187,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -249,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -263,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -285,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -295,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -305,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -347,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -359,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -383,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.19.orig/include/linux/auto_fs4.h ++++ linux-2.6.19/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -59,6 +118,13 @@ struct autofs_packet_expire_multi { + char name[NAME_MAX+1]; + }; + ++union autofs_packet_union { ++ struct autofs_packet_hdr hdr; ++ struct autofs_packet_missing missing; ++ struct autofs_packet_expire expire; ++ struct autofs_packet_expire_multi expire_multi; ++}; ++ + /* autofs v5 common packet struct */ + struct autofs_v5_packet { + struct autofs_packet_hdr hdr; +@@ -78,20 +144,19 @@ typedef struct autofs_v5_packet autofs_p + typedef struct autofs_v5_packet autofs_packet_missing_direct_t; + typedef struct autofs_v5_packet autofs_packet_expire_direct_t; + +-union autofs_packet_union { ++union autofs_v5_packet_union { + struct autofs_packet_hdr hdr; +- struct autofs_packet_missing missing; +- struct autofs_packet_expire expire; +- struct autofs_packet_expire_multi expire_multi; + struct autofs_v5_packet v5_packet; ++ autofs_packet_missing_indirect_t missing_indirect; ++ autofs_packet_expire_indirect_t expire_indirect; ++ autofs_packet_missing_direct_t missing_direct; ++ autofs_packet_expire_direct_t expire_direct; + }; + + #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- linux-2.6.19.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.19/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,10 +72,18 @@ struct autofs_info { + + int flags; + ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; ++ + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -66,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -83,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -110,6 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -134,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -159,11 +181,23 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.19.orig/fs/autofs4/root.c ++++ linux-2.6.19/fs/autofs4/root.c +@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_dentry; +- struct vfsmount *mnt = file->f_vfsmnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -114,157 +82,30 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -ENOENT; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -292,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -319,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -334,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -402,21 +257,33 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ + status = try_to_fill_dentry(dentry, flags); + if (status == 0) +- return 1; ++ return 1; + + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -430,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -459,6 +327,17 @@ void autofs4_dentry_release(struct dentr + de->d_fsdata = NULL; + + if (inf) { ++ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); ++ ++ if (sbi) { ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ } ++ + inf->dentry = NULL; + inf->inode = NULL; + +@@ -478,10 +357,116 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Bad luck, we've already been dentry_iput */ ++ if (!dentry->d_inode) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ + /* Lookups in the root directory */ + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -497,29 +482,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. +- */ +- dentry->d_op = &autofs4_root_dentry_operations; ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { ++ /* ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. ++ */ ++ dentry->d_op = &autofs4_root_dentry_operations; ++ ++ /* ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. ++ */ ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); ++ } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -534,22 +557,47 @@ static struct dentry *autofs4_lookup(str + if (sigismember (sigset, SIGKILL) || + sigismember (sigset, SIGQUIT) || + sigismember (sigset, SIGINT)) { ++ if (unhashed) ++ dput(unhashed); + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* + * If this dentry is unhashed, then we shouldn't honour this +- * lookup even if the dentry is positive. Returning ENOENT here +- * doesn't do the right thing for all system calls, but it should +- * be OK for the operations we permit from an autofs. ++ * lookup. Returning ENOENT here doesn't do the right thing ++ * for all system calls, but it should be OK for the operations ++ * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) +- return ERR_PTR(-ENOENT); ++ if (!oz_mode && d_unhashed(dentry)) { ++ /* ++ * A user space application can (and has done in the past) ++ * remove and re-create this directory during the callback. ++ * This can leave us with an unhashed dentry, but a ++ * successful mount! So we need to perform another ++ * cached lookup in case the dentry now exists. ++ */ ++ struct dentry *parent = dentry->d_parent; ++ struct dentry *new = d_lookup(parent, &dentry->d_name); ++ if (new != NULL) ++ dentry = new; ++ else ++ dentry = ERR_PTR(-ENOENT); ++ ++ if (unhashed) ++ dput(unhashed); ++ ++ return dentry; ++ } ++ ++ if (unhashed) ++ return unhashed; + + return NULL; + } +@@ -571,21 +619,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -600,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -611,9 +671,9 @@ static int autofs4_dir_symlink(struct in + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want +- * this, because since the unlink is probably the result of an expire. +- * We simply d_drop it, which allows the dentry lookup to remount it +- * if necessary. ++ * this, because the unlink is probably the result of an expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -642,7 +702,15 @@ static int autofs4_dir_unlink(struct ino + + dir->i_mtime = CURRENT_TIME; + +- d_drop(dentry); ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); ++ spin_lock(&dentry->d_lock); ++ __d_drop(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&dcache_lock); + + return 0; + } +@@ -653,6 +721,9 @@ static int autofs4_dir_rmdir(struct inod + struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; + ++ DPRINTK("dentry %p, removing %.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ + if (!autofs4_oz_mode(sbi)) + return -EACCES; + +@@ -661,6 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -695,11 +770,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -751,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if ( status ) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -852,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_vfsmnt, p); + +--- linux-2.6.19.orig/fs/autofs4/expire.c ++++ linux-2.6.19/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,9 +469,15 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + + /* Call repeatedly until it returns -EAGAIN, meaning there's nothing +@@ -425,7 +492,7 @@ int autofs4_expire_multi(struct super_bl + if (arg && get_user(do_now, arg)) + return -EFAULT; + +- if (sbi->type & AUTOFS_TYPE_DIRECT) ++ if (autofs_type_trigger(sbi->type)) + dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); + else + dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); +@@ -435,9 +502,16 @@ int autofs4_expire_multi(struct super_bl + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + +--- linux-2.6.19.orig/include/linux/compat_ioctl.h ++++ linux-2.6.19/include/linux/compat_ioctl.h +@@ -568,8 +568,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- /dev/null ++++ linux-2.6.19/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.19.orig/fs/autofs4/Makefile ++++ linux-2.6.19/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.19/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,867 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct dentry *dentry; ++ struct vfsmount *mnt; ++ int err = -EAGAIN; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_vfsmnt; ++ ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sbi->sb, mnt, sbi, how); ++ else ++ dentry = autofs4_expire_indirect(sbi->sb, mnt, sbi, how); ++ ++ if (dentry) { ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ ++ /* ++ * This is synchronous because it makes the daemon a ++ * little easier ++ */ ++ err = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ sbi->sb->s_root->d_mounted++; ++ } ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ dput(dentry); ++ } ++ ++ return err; ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_vfsmnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t devid = new_encode_dev(sbi->sb->s_dev); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ devid = autofs4_get_dev(sbi); ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.19.orig/fs/autofs4/init.c ++++ linux-2.6.19/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.19/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.19.orig/include/linux/auto_fs.h ++++ linux-2.6.19/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.20-v5-update-20081027.patch b/patches/autofs4-2.6.20-v5-update-20081027.patch deleted file mode 100644 index 80a4342..0000000 --- a/patches/autofs4-2.6.20-v5-update-20081027.patch +++ /dev/null @@ -1,1857 +0,0 @@ -Index: linux-2.6.20/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.20.orig/fs/autofs4/waitq.c -+++ linux-2.6.20/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -84,11 +95,16 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -98,26 +114,26 @@ static void autofs4_notify_daemon(struct - /* Kernel protocol v4 missing and expire packets */ - case autofs_ptype_missing: - { -- struct autofs_packet_missing *mp = &pkt.missing; -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: - { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -129,14 +145,14 @@ static void autofs4_notify_daemon(struct - case autofs_ptype_missing_direct: - case autofs_ptype_expire_direct: - { -- struct autofs_v5_packet *packet = &pkt.v5_packet; -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; - - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -150,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -167,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -187,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -249,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -263,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -285,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -295,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -305,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -347,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -360,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -383,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.20/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.20.orig/include/linux/auto_fs4.h -+++ linux-2.6.20/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -59,6 +64,13 @@ struct autofs_packet_expire_multi { - char name[NAME_MAX+1]; - }; - -+union autofs_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_packet_missing missing; -+ struct autofs_packet_expire expire; -+ struct autofs_packet_expire_multi expire_multi; -+}; -+ - /* autofs v5 common packet struct */ - struct autofs_v5_packet { - struct autofs_packet_hdr hdr; -@@ -78,20 +90,19 @@ typedef struct autofs_v5_packet autofs_p - typedef struct autofs_v5_packet autofs_packet_missing_direct_t; - typedef struct autofs_v5_packet autofs_packet_expire_direct_t; - --union autofs_packet_union { -+union autofs_v5_packet_union { - struct autofs_packet_hdr hdr; -- struct autofs_packet_missing missing; -- struct autofs_packet_expire expire; -- struct autofs_packet_expire_multi expire_multi; - struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; - }; - - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.20/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.20.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.20/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,6 +54,11 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -66,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -83,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -110,6 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -134,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -160,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.20/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.20.orig/fs/autofs4/inode.c -+++ linux-2.6.20/fs/autofs4/inode.c -@@ -25,8 +25,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -42,14 +44,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -158,14 +164,13 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- sb->s_fs_info = NULL; -- -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - - out_kill_sb: -@@ -280,7 +285,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -330,12 +335,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -370,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.20/fs/autofs4/root.c -=================================================================== ---- linux-2.6.20.orig/fs/autofs4/root.c -+++ linux-2.6.20/fs/autofs4/root.c -@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -114,157 +82,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -- } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -ENOENT; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -292,7 +134,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -319,7 +162,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -334,50 +178,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -402,21 +258,33 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) -- return 1; -+ return 1; - - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -430,6 +298,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -459,6 +328,17 @@ void autofs4_dentry_release(struct dentr - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -478,10 +358,116 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ - /* Lookups in the root directory */ - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -497,30 +483,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -534,22 +557,47 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -571,21 +619,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -600,6 +659,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -611,9 +671,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -642,7 +702,15 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -653,6 +721,9 @@ static int autofs4_dir_rmdir(struct inod - struct autofs_info *ino = autofs4_dentry_ino(dentry); - struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -661,6 +732,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -695,11 +770,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -751,44 +836,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -852,11 +899,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.20/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.20.orig/fs/autofs4/expire.c -+++ linux-2.6.20/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.20/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.20.orig/include/linux/compat_ioctl.h -+++ linux-2.6.20/include/linux/compat_ioctl.h -@@ -568,8 +568,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) diff --git a/patches/autofs4-2.6.20-v5-update-20090903.patch b/patches/autofs4-2.6.20-v5-update-20090903.patch new file mode 100644 index 0000000..f0605ea --- /dev/null +++ b/patches/autofs4-2.6.20-v5-update-20090903.patch @@ -0,0 +1,3621 @@ +--- linux-2.6.20.orig/fs/autofs4/waitq.c ++++ linux-2.6.20/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -84,11 +95,16 @@ static void autofs4_notify_daemon(struct + struct autofs_wait_queue *wq, + int type) + { +- union autofs_packet_union pkt; ++ union { ++ struct autofs_packet_hdr hdr; ++ union autofs_packet_union v4_pkt; ++ union autofs_v5_packet_union v5_pkt; ++ } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -98,26 +114,26 @@ static void autofs4_notify_daemon(struct + /* Kernel protocol v4 missing and expire packets */ + case autofs_ptype_missing: + { +- struct autofs_packet_missing *mp = &pkt.missing; ++ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; + + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: + { +- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; ++ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; + + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -129,14 +145,14 @@ static void autofs4_notify_daemon(struct + case autofs_ptype_missing_direct: + case autofs_ptype_expire_direct: + { +- struct autofs_v5_packet *packet = &pkt.v5_packet; ++ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; + + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -150,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -167,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -187,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -249,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -263,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -285,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -295,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -305,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -347,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -359,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -383,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.20.orig/include/linux/auto_fs4.h ++++ linux-2.6.20/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -59,6 +118,13 @@ struct autofs_packet_expire_multi { + char name[NAME_MAX+1]; + }; + ++union autofs_packet_union { ++ struct autofs_packet_hdr hdr; ++ struct autofs_packet_missing missing; ++ struct autofs_packet_expire expire; ++ struct autofs_packet_expire_multi expire_multi; ++}; ++ + /* autofs v5 common packet struct */ + struct autofs_v5_packet { + struct autofs_packet_hdr hdr; +@@ -78,20 +144,19 @@ typedef struct autofs_v5_packet autofs_p + typedef struct autofs_v5_packet autofs_packet_missing_direct_t; + typedef struct autofs_v5_packet autofs_packet_expire_direct_t; + +-union autofs_packet_union { ++union autofs_v5_packet_union { + struct autofs_packet_hdr hdr; +- struct autofs_packet_missing missing; +- struct autofs_packet_expire expire; +- struct autofs_packet_expire_multi expire_multi; + struct autofs_v5_packet v5_packet; ++ autofs_packet_missing_indirect_t missing_indirect; ++ autofs_packet_expire_indirect_t expire_indirect; ++ autofs_packet_missing_direct_t missing_direct; ++ autofs_packet_expire_direct_t expire_direct; + }; + + #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- linux-2.6.20.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.20/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,10 +72,18 @@ struct autofs_info { + + int flags; + ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; ++ + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -66,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -83,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -110,6 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -134,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -160,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.20.orig/fs/autofs4/inode.c ++++ linux-2.6.20/fs/autofs4/inode.c +@@ -25,8 +25,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -42,14 +44,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -158,14 +166,13 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- sb->s_fs_info = NULL; +- +- if ( !sbi->catatonic ) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); + ++ sb->s_fs_info = NULL; + kfree(sbi); + + out_kill_sb: +@@ -186,9 +193,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -274,13 +281,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -330,12 +337,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -370,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.20.orig/fs/autofs4/root.c ++++ linux-2.6.20/fs/autofs4/root.c +@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -71,42 +71,10 @@ struct inode_operations autofs4_dir_inod + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -114,157 +82,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -ENOENT; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -292,7 +134,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -319,7 +162,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -334,50 +178,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -402,21 +258,33 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ + status = try_to_fill_dentry(dentry, flags); + if (status == 0) +- return 1; ++ return 1; + + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -430,6 +298,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -459,6 +328,17 @@ void autofs4_dentry_release(struct dentr + de->d_fsdata = NULL; + + if (inf) { ++ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); ++ ++ if (sbi) { ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ } ++ + inf->dentry = NULL; + inf->inode = NULL; + +@@ -478,10 +358,116 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Bad luck, we've already been dentry_iput */ ++ if (!dentry->d_inode) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ + /* Lookups in the root directory */ + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -497,29 +483,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. +- */ +- dentry->d_op = &autofs4_root_dentry_operations; ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { ++ /* ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. ++ */ ++ dentry->d_op = &autofs4_root_dentry_operations; ++ ++ /* ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. ++ */ ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); ++ } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -534,22 +558,47 @@ static struct dentry *autofs4_lookup(str + if (sigismember (sigset, SIGKILL) || + sigismember (sigset, SIGQUIT) || + sigismember (sigset, SIGINT)) { ++ if (unhashed) ++ dput(unhashed); + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* + * If this dentry is unhashed, then we shouldn't honour this +- * lookup even if the dentry is positive. Returning ENOENT here +- * doesn't do the right thing for all system calls, but it should +- * be OK for the operations we permit from an autofs. ++ * lookup. Returning ENOENT here doesn't do the right thing ++ * for all system calls, but it should be OK for the operations ++ * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) +- return ERR_PTR(-ENOENT); ++ if (!oz_mode && d_unhashed(dentry)) { ++ /* ++ * A user space application can (and has done in the past) ++ * remove and re-create this directory during the callback. ++ * This can leave us with an unhashed dentry, but a ++ * successful mount! So we need to perform another ++ * cached lookup in case the dentry now exists. ++ */ ++ struct dentry *parent = dentry->d_parent; ++ struct dentry *new = d_lookup(parent, &dentry->d_name); ++ if (new != NULL) ++ dentry = new; ++ else ++ dentry = ERR_PTR(-ENOENT); ++ ++ if (unhashed) ++ dput(unhashed); ++ ++ return dentry; ++ } ++ ++ if (unhashed) ++ return unhashed; + + return NULL; + } +@@ -571,21 +620,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -600,6 +660,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -611,9 +672,9 @@ static int autofs4_dir_symlink(struct in + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want +- * this, because since the unlink is probably the result of an expire. +- * We simply d_drop it, which allows the dentry lookup to remount it +- * if necessary. ++ * this, because the unlink is probably the result of an expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -642,7 +703,15 @@ static int autofs4_dir_unlink(struct ino + + dir->i_mtime = CURRENT_TIME; + +- d_drop(dentry); ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); ++ spin_lock(&dentry->d_lock); ++ __d_drop(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&dcache_lock); + + return 0; + } +@@ -653,6 +722,9 @@ static int autofs4_dir_rmdir(struct inod + struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_info *p_ino; + ++ DPRINTK("dentry %p, removing %.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ + if (!autofs4_oz_mode(sbi)) + return -EACCES; + +@@ -661,6 +733,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -695,11 +771,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -751,44 +837,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if ( status ) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -852,11 +900,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.20.orig/fs/autofs4/expire.c ++++ linux-2.6.20/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.20.orig/include/linux/compat_ioctl.h ++++ linux-2.6.20/include/linux/compat_ioctl.h +@@ -568,8 +568,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- /dev/null ++++ linux-2.6.20/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.20.orig/fs/autofs4/Makefile ++++ linux-2.6.20/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.20/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.20.orig/fs/autofs4/init.c ++++ linux-2.6.20/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.20/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.20.orig/include/linux/auto_fs.h ++++ linux-2.6.20/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.21-v5-update-20090903.patch b/patches/autofs4-2.6.21-v5-update-20090903.patch new file mode 100644 index 0000000..faff95b --- /dev/null +++ b/patches/autofs4-2.6.21-v5-update-20090903.patch @@ -0,0 +1,3564 @@ +--- linux-2.6.21.orig/fs/autofs4/root.c ++++ linux-2.6.21/fs/autofs4/root.c +@@ -26,25 +26,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -71,42 +71,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -114,157 +82,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -292,7 +134,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -319,7 +162,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -334,50 +178,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -402,12 +258,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -415,17 +282,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -439,6 +298,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -471,10 +331,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -496,7 +358,59 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -504,14 +418,14 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -533,33 +447,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -569,7 +466,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -585,50 +483,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- d_rehash(unhashed); +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -648,9 +563,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -659,7 +576,7 @@ static struct dentry *autofs4_lookup(str + * for all system calls, but it should be OK for the operations + * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) { ++ if (!oz_mode && d_unhashed(dentry)) { + /* + * A user space application can (and has done in the past) + * remove and re-create this directory during the callback. +@@ -681,7 +598,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -703,21 +620,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -732,6 +660,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -744,9 +673,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -776,9 +704,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -804,9 +733,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -841,11 +771,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -897,44 +837,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if ( status ) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -998,11 +900,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.21.orig/fs/autofs4/waitq.c ++++ linux-2.6.21/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.21.orig/fs/autofs4/expire.c ++++ linux-2.6.21/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.21.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.21/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.21.orig/fs/autofs4/inode.c ++++ linux-2.6.21/fs/autofs4/inode.c +@@ -25,8 +25,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -42,16 +44,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -160,8 +166,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -187,9 +193,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -275,13 +281,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -331,14 +337,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -373,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.21.orig/include/linux/auto_fs4.h ++++ linux-2.6.21/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- linux-2.6.21.orig/include/linux/compat_ioctl.h ++++ linux-2.6.21/include/linux/compat_ioctl.h +@@ -568,8 +568,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- /dev/null ++++ linux-2.6.21/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.21.orig/fs/autofs4/Makefile ++++ linux-2.6.21/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.21/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.21.orig/fs/autofs4/init.c ++++ linux-2.6.21/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.21/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.21.orig/include/linux/auto_fs.h ++++ linux-2.6.21/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.22-dev-ioctl-20081029.patch b/patches/autofs4-2.6.22-dev-ioctl-20081029.patch deleted file mode 100644 index 1f15936..0000000 --- a/patches/autofs4-2.6.22-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.22/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.22/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.22/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/inode.c -+++ linux-2.6.22/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -190,9 +192,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -277,13 +279,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -333,7 +335,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -375,7 +377,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.22/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/waitq.c -+++ linux-2.6.22/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.22/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.22/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.22/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/Makefile -+++ linux-2.6.22/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.22/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.22/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ inode = nd->dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ ino = autofs4_dentry_ino(nd->dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_release(&nd); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.dentry->d_inode && -+ nd.mnt->mnt_root == nd.dentry) { -+ err = 1; -+ magic = nd.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.dentry); -+ -+ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { -+ if (follow_down(&nd.mnt, &nd.dentry)) { -+ struct inode *inode = nd.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.22/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/expire.c -+++ linux-2.6.22/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.22/fs/autofs4/init.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/init.c -+++ linux-2.6.22/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.22/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.22/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.22/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.22.orig/include/linux/auto_fs4.h -+++ linux-2.6.22/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.22-v5-update-20081027.patch b/patches/autofs4-2.6.22-v5-update-20081027.patch deleted file mode 100644 index d733ded..0000000 --- a/patches/autofs4-2.6.22-v5-update-20081027.patch +++ /dev/null @@ -1,1799 +0,0 @@ -Index: linux-2.6.22/fs/autofs4/root.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/root.c -+++ linux-2.6.22/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -495,7 +357,59 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -503,14 +417,14 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -568,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -584,51 +482,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -- -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. - */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- d_rehash(unhashed); -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) - dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -647,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -658,7 +574,7 @@ static struct dentry *autofs4_lookup(str - * for all system calls, but it should be OK for the operations - * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) { -+ if (!oz_mode && d_unhashed(dentry)) { - /* - * A user space application can (and has done in the past) - * remove and re-create this directory during the callback. -@@ -680,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -702,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -731,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -743,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -775,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -803,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -840,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -896,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -997,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.22/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/waitq.c -+++ linux-2.6.22/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.22/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/expire.c -+++ linux-2.6.22/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.22/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.22/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.22/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.22.orig/fs/autofs4/inode.c -+++ linux-2.6.22/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -279,7 +283,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -329,14 +333,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -370,7 +375,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.22/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.22.orig/fs/compat_ioctl.c -+++ linux-2.6.22/fs/compat_ioctl.c -@@ -2998,8 +2998,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.22/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.22.orig/include/linux/auto_fs4.h -+++ linux-2.6.22/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.22-v5-update-20090903.patch b/patches/autofs4-2.6.22-v5-update-20090903.patch new file mode 100644 index 0000000..d78a915 --- /dev/null +++ b/patches/autofs4-2.6.22-v5-update-20090903.patch @@ -0,0 +1,3564 @@ +--- linux-2.6.22.orig/fs/autofs4/root.c ++++ linux-2.6.22/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -495,7 +357,59 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -503,14 +417,14 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -568,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -584,50 +482,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- d_rehash(unhashed); +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -647,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -658,7 +575,7 @@ static struct dentry *autofs4_lookup(str + * for all system calls, but it should be OK for the operations + * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) { ++ if (!oz_mode && d_unhashed(dentry)) { + /* + * A user space application can (and has done in the past) + * remove and re-create this directory during the callback. +@@ -680,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -702,21 +619,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -731,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -743,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -775,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -803,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -840,11 +770,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -896,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -997,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.22.orig/fs/autofs4/waitq.c ++++ linux-2.6.22/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.22.orig/fs/autofs4/expire.c ++++ linux-2.6.22/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.22.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.22/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.22.orig/fs/autofs4/inode.c ++++ linux-2.6.22/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -186,9 +192,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -273,13 +279,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -329,14 +335,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -370,7 +377,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.22.orig/fs/compat_ioctl.c ++++ linux-2.6.22/fs/compat_ioctl.c +@@ -2998,8 +2998,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.22.orig/include/linux/auto_fs4.h ++++ linux-2.6.22/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.22/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.22.orig/fs/autofs4/Makefile ++++ linux-2.6.22/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.22/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.22.orig/fs/autofs4/init.c ++++ linux-2.6.22/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.22/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.22.orig/include/linux/auto_fs.h ++++ linux-2.6.22/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.22.17-dev-ioctl-20081029.patch b/patches/autofs4-2.6.22.17-dev-ioctl-20081029.patch deleted file mode 100644 index 54318b4..0000000 --- a/patches/autofs4-2.6.22.17-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.22.17/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.22.17/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.22.17/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/inode.c -+++ linux-2.6.22.17/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -190,9 +192,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -277,13 +279,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -333,7 +335,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -375,7 +377,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.22.17/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/waitq.c -+++ linux-2.6.22.17/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.22.17/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.22.17/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.22.17/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/Makefile -+++ linux-2.6.22.17/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.22.17/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.22.17/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ inode = nd->dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ ino = autofs4_dentry_ino(nd->dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_release(&nd); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.dentry->d_inode && -+ nd.mnt->mnt_root == nd.dentry) { -+ err = 1; -+ magic = nd.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.dentry); -+ -+ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { -+ if (follow_down(&nd.mnt, &nd.dentry)) { -+ struct inode *inode = nd.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.22.17/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/expire.c -+++ linux-2.6.22.17/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.22.17/fs/autofs4/init.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/init.c -+++ linux-2.6.22.17/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.22.17/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.22.17/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.22.17/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.22.17.orig/include/linux/auto_fs4.h -+++ linux-2.6.22.17/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.22.17-v5-update-20081027.patch b/patches/autofs4-2.6.22.17-v5-update-20081027.patch deleted file mode 100644 index 6de0e23..0000000 --- a/patches/autofs4-2.6.22.17-v5-update-20081027.patch +++ /dev/null @@ -1,1799 +0,0 @@ -Index: linux-2.6.22.17/fs/autofs4/root.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/root.c -+++ linux-2.6.22.17/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -495,7 +357,59 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -503,14 +417,14 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -568,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -584,51 +482,67 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -- -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. - */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- d_rehash(unhashed); -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) - dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -647,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -658,7 +574,7 @@ static struct dentry *autofs4_lookup(str - * for all system calls, but it should be OK for the operations - * we permit from an autofs. - */ -- if (dentry->d_inode && d_unhashed(dentry)) { -+ if (!oz_mode && d_unhashed(dentry)) { - /* - * A user space application can (and has done in the past) - * remove and re-create this directory during the callback. -@@ -680,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -702,21 +618,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -731,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -743,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -775,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -803,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -840,11 +769,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -896,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -997,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.22.17/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/waitq.c -+++ linux-2.6.22.17/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.22.17/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/expire.c -+++ linux-2.6.22.17/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.22.17/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.22.17/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.22.17/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.22.17.orig/fs/autofs4/inode.c -+++ linux-2.6.22.17/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -279,7 +283,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -329,14 +333,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -370,7 +375,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.22.17/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.22.17.orig/fs/compat_ioctl.c -+++ linux-2.6.22.17/fs/compat_ioctl.c -@@ -2998,8 +2998,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.22.17/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.22.17.orig/include/linux/auto_fs4.h -+++ linux-2.6.22.17/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.22.17-v5-update-20090903.patch b/patches/autofs4-2.6.22.17-v5-update-20090903.patch new file mode 100644 index 0000000..a11dd18 --- /dev/null +++ b/patches/autofs4-2.6.22.17-v5-update-20090903.patch @@ -0,0 +1,3564 @@ +--- linux-2.6.22.17.orig/fs/autofs4/root.c ++++ linux-2.6.22.17/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -495,7 +357,59 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -503,14 +417,14 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -568,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -584,50 +482,67 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* +- * Mark the dentry incomplete, but add it. This is needed so +- * that the VFS layer knows about the dentry, and we can count +- * on catching any lookups through the revalidate. +- * +- * Let all the hard work be done by the revalidate function that +- * needs to be able to do this anyway.. +- * +- * We need to do this before we release the directory semaphore. ++ * Mark the dentry incomplete but don't hash it. We do this ++ * to serialize our inode creation operations (symlink and ++ * mkdir) which prevents deadlock during the callback to ++ * the daemon. Subsequent user space lookups for the same ++ * dentry are placed on the wait queue while the daemon ++ * itself is allowed passage unresticted so the create ++ * operation itself can then hash the dentry. Finally, ++ * we check for the hashed dentry and return the newly ++ * hashed dentry. + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_add(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- d_rehash(unhashed); +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -647,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -658,7 +575,7 @@ static struct dentry *autofs4_lookup(str + * for all system calls, but it should be OK for the operations + * we permit from an autofs. + */ +- if (dentry->d_inode && d_unhashed(dentry)) { ++ if (!oz_mode && d_unhashed(dentry)) { + /* + * A user space application can (and has done in the past) + * remove and re-create this directory during the callback. +@@ -680,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -702,21 +619,32 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -731,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -743,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -775,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -803,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -840,11 +770,21 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); +- d_instantiate(dentry, inode); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } ++ d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) + dentry->d_op = &autofs4_root_dentry_operations; +@@ -896,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -997,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.22.17.orig/fs/autofs4/waitq.c ++++ linux-2.6.22.17/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.22.17.orig/fs/autofs4/expire.c ++++ linux-2.6.22.17/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.22.17.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.22.17/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.22.17.orig/fs/autofs4/inode.c ++++ linux-2.6.22.17/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -186,9 +192,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -273,13 +279,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -329,14 +335,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -370,7 +377,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.22.17.orig/fs/compat_ioctl.c ++++ linux-2.6.22.17/fs/compat_ioctl.c +@@ -2998,8 +2998,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.22.17.orig/include/linux/auto_fs4.h ++++ linux-2.6.22.17/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.22.17/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.22.17.orig/fs/autofs4/Makefile ++++ linux-2.6.22.17/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.22.17/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.22.17.orig/fs/autofs4/init.c ++++ linux-2.6.22.17/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.22.17/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.22.17.orig/include/linux/auto_fs.h ++++ linux-2.6.22.17/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.23-dev-ioctl-20081029.patch b/patches/autofs4-2.6.23-dev-ioctl-20081029.patch deleted file mode 100644 index 4d7d24f..0000000 --- a/patches/autofs4-2.6.23-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.23/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.23/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.23/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/inode.c -+++ linux-2.6.23/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -190,9 +192,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -277,13 +279,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -333,7 +335,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -375,7 +377,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.23/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/waitq.c -+++ linux-2.6.23/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.23/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.23/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.23/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/Makefile -+++ linux-2.6.23/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.23/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.23/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ inode = nd->dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ ino = autofs4_dentry_ino(nd->dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_release(&nd); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.dentry->d_inode && -+ nd.mnt->mnt_root == nd.dentry) { -+ err = 1; -+ magic = nd.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.dentry); -+ -+ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { -+ if (follow_down(&nd.mnt, &nd.dentry)) { -+ struct inode *inode = nd.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.23/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/expire.c -+++ linux-2.6.23/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.23/fs/autofs4/init.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/init.c -+++ linux-2.6.23/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.23/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.23/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.23/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.23.orig/include/linux/auto_fs4.h -+++ linux-2.6.23/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.23-v5-update-20081027.patch b/patches/autofs4-2.6.23-v5-update-20081027.patch deleted file mode 100644 index c422c60..0000000 --- a/patches/autofs4-2.6.23-v5-update-20081027.patch +++ /dev/null @@ -1,1774 +0,0 @@ -Index: linux-2.6.23/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/waitq.c -+++ linux-2.6.23/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.23/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/expire.c -+++ linux-2.6.23/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.23/fs/autofs4/root.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/root.c -+++ linux-2.6.23/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -568,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -584,8 +482,26 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { - /* - * Mark the dentry incomplete but don't hash it. We do this - * to serialize our inode creation operations (symlink and -@@ -599,39 +515,34 @@ static struct dentry *autofs4_lookup(str - */ - dentry->d_op = &autofs4_root_dentry_operations; - -- dentry->d_fsdata = NULL; -- d_instantiate(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); - /* -- * 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, so just wait for it. -- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear -- * before continuing as revalidate may fail when calling -- * try_to_fill_dentry (returning EAGAIN) if we don't. -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. - */ -- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- dentry = unhashed; -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -650,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -683,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -705,20 +618,31 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -734,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -746,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -778,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -806,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -843,10 +769,20 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -899,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -1000,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.23/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.23/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.23/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.23.orig/fs/autofs4/inode.c -+++ linux-2.6.23/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -279,7 +283,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -329,14 +333,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -370,7 +375,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.23/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.23.orig/fs/compat_ioctl.c -+++ linux-2.6.23/fs/compat_ioctl.c -@@ -3017,8 +3017,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.23/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.23.orig/include/linux/auto_fs4.h -+++ linux-2.6.23/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.23-v5-update-20090903.patch b/patches/autofs4-2.6.23-v5-update-20090903.patch new file mode 100644 index 0000000..f37af94 --- /dev/null +++ b/patches/autofs4-2.6.23-v5-update-20090903.patch @@ -0,0 +1,3539 @@ +--- linux-2.6.23.orig/fs/autofs4/waitq.c ++++ linux-2.6.23/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.23.orig/fs/autofs4/expire.c ++++ linux-2.6.23/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.23.orig/fs/autofs4/root.c ++++ linux-2.6.23/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -568,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -584,8 +482,10 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, process_group(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* + * Mark the dentry incomplete but don't hash it. We do this + * to serialize our inode creation operations (symlink and +@@ -599,38 +499,50 @@ static struct dentry *autofs4_lookup(str + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_instantiate(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. +- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear +- * before continuing as revalidate may fail when calling +- * try_to_fill_dentry (returning EAGAIN) if we don't. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -650,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -683,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -705,20 +619,31 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -734,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -746,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -778,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -806,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -843,10 +770,20 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -899,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -1000,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.23.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.23/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.23.orig/fs/autofs4/inode.c ++++ linux-2.6.23/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -186,9 +192,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -273,13 +279,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -329,14 +335,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -370,7 +377,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.23.orig/fs/compat_ioctl.c ++++ linux-2.6.23/fs/compat_ioctl.c +@@ -3017,8 +3017,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.23.orig/include/linux/auto_fs4.h ++++ linux-2.6.23/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.23/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.23.orig/fs/autofs4/Makefile ++++ linux-2.6.23/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.23/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = process_group(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.23.orig/fs/autofs4/init.c ++++ linux-2.6.23/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.23/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.23.orig/include/linux/auto_fs.h ++++ linux-2.6.23/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.24-dev-ioctl-20081029.patch b/patches/autofs4-2.6.24-dev-ioctl-20081029.patch deleted file mode 100644 index 0db5896..0000000 --- a/patches/autofs4-2.6.24-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.24/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.24/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.24/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/inode.c -+++ linux-2.6.24/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -190,9 +192,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -277,13 +279,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -331,7 +333,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -373,7 +375,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.24/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/waitq.c -+++ linux-2.6.24/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.24/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.24/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.24/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/Makefile -+++ linux-2.6.24/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.24/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.24/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ inode = nd->dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ ino = autofs4_dentry_ino(nd->dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_release(&nd); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.dentry->d_inode && -+ nd.mnt->mnt_root == nd.dentry) { -+ err = 1; -+ magic = nd.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.dentry); -+ -+ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { -+ if (follow_down(&nd.mnt, &nd.dentry)) { -+ struct inode *inode = nd.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.24/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/expire.c -+++ linux-2.6.24/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.24/fs/autofs4/init.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/init.c -+++ linux-2.6.24/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.24/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.24/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.24/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.24.orig/include/linux/auto_fs4.h -+++ linux-2.6.24/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.24-v5-update-20081027.patch b/patches/autofs4-2.6.24-v5-update-20081027.patch deleted file mode 100644 index b772967..0000000 --- a/patches/autofs4-2.6.24-v5-update-20081027.patch +++ /dev/null @@ -1,1774 +0,0 @@ -Index: linux-2.6.24/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/waitq.c -+++ linux-2.6.24/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.24/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/expire.c -+++ linux-2.6.24/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.24/fs/autofs4/root.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/root.c -+++ linux-2.6.24/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -568,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -584,8 +482,26 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { - /* - * Mark the dentry incomplete but don't hash it. We do this - * to serialize our inode creation operations (symlink and -@@ -599,39 +515,34 @@ static struct dentry *autofs4_lookup(str - */ - dentry->d_op = &autofs4_root_dentry_operations; - -- dentry->d_fsdata = NULL; -- d_instantiate(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); - /* -- * 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, so just wait for it. -- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear -- * before continuing as revalidate may fail when calling -- * try_to_fill_dentry (returning EAGAIN) if we don't. -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. - */ -- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- dentry = unhashed; -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -650,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -683,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -705,20 +618,31 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -734,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -746,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -778,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -806,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -843,10 +769,20 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -899,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -1000,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.24/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.24/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.24/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.24.orig/fs/autofs4/inode.c -+++ linux-2.6.24/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -279,7 +283,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -327,14 +331,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -368,7 +373,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.24/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.24.orig/fs/compat_ioctl.c -+++ linux-2.6.24/fs/compat_ioctl.c -@@ -2384,8 +2384,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.24/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.24.orig/include/linux/auto_fs4.h -+++ linux-2.6.24/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.24-v5-update-20090903.patch b/patches/autofs4-2.6.24-v5-update-20090903.patch new file mode 100644 index 0000000..448bcd3 --- /dev/null +++ b/patches/autofs4-2.6.24-v5-update-20090903.patch @@ -0,0 +1,3539 @@ +--- linux-2.6.24.orig/fs/autofs4/waitq.c ++++ linux-2.6.24/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.24.orig/fs/autofs4/expire.c ++++ linux-2.6.24/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.24.orig/fs/autofs4/root.c ++++ linux-2.6.24/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -568,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -584,8 +482,10 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* + * Mark the dentry incomplete but don't hash it. We do this + * to serialize our inode creation operations (symlink and +@@ -599,38 +499,50 @@ static struct dentry *autofs4_lookup(str + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_instantiate(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. +- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear +- * before continuing as revalidate may fail when calling +- * try_to_fill_dentry (returning EAGAIN) if we don't. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -650,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -683,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -705,20 +619,31 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -734,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -746,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -778,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -806,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -843,10 +770,20 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -899,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -1000,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.24.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.24/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.24.orig/fs/autofs4/inode.c ++++ linux-2.6.24/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -186,9 +192,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -273,13 +279,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -327,14 +333,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -368,7 +375,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.24.orig/fs/compat_ioctl.c ++++ linux-2.6.24/fs/compat_ioctl.c +@@ -2384,8 +2384,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.24.orig/include/linux/auto_fs4.h ++++ linux-2.6.24/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.24/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.24.orig/fs/autofs4/Makefile ++++ linux-2.6.24/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.24/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.24.orig/fs/autofs4/init.c ++++ linux-2.6.24/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.24/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.24.orig/include/linux/auto_fs.h ++++ linux-2.6.24/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.24.4-dev-ioctl-20081029.patch b/patches/autofs4-2.6.24.4-dev-ioctl-20081029.patch deleted file mode 100644 index fe7ef5a..0000000 --- a/patches/autofs4-2.6.24.4-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1884 +0,0 @@ -Index: linux-2.6.24.4/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.24.4/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.24.4/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/inode.c -+++ linux-2.6.24.4/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -190,9 +192,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -277,13 +279,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(*type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(*type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(*type); - break; - default: - return 1; -@@ -331,7 +333,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -373,7 +375,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.24.4/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/waitq.c -+++ linux-2.6.24.4/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.24.4/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.24.4/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.24.4/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/Makefile -+++ linux-2.6.24.4/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.24.4/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.24.4/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ inode = nd->dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->dentry); -+ nd->dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->mnt, &nd->dentry)) { -+ ino = autofs4_dentry_ino(nd->dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_release(&nd); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.dentry->d_inode && -+ nd.mnt->mnt_root == nd.dentry) { -+ err = 1; -+ magic = nd.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.dentry); -+ -+ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { -+ if (follow_down(&nd.mnt, &nd.dentry)) { -+ struct inode *inode = nd.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_release(&nd); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.24.4/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/expire.c -+++ linux-2.6.24.4/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.24.4/fs/autofs4/init.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/init.c -+++ linux-2.6.24.4/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.24.4/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.24.4/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.24.4/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.24.4.orig/include/linux/auto_fs4.h -+++ linux-2.6.24.4/include/linux/auto_fs4.h -@@ -23,16 +23,36 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+#define set_autofs_type_indirect(type) (type = AUTOFS_TYPE_INDIRECT) -+#define autofs_type_indirect(type) (type == AUTOFS_TYPE_INDIRECT) -+ -+#define set_autofs_type_direct(type) (type = AUTOFS_TYPE_DIRECT) -+#define autofs_type_direct(type) (type == AUTOFS_TYPE_DIRECT) -+ -+#define set_autofs_type_offset(type) (type = AUTOFS_TYPE_OFFSET) -+#define autofs_type_offset(type) (type == AUTOFS_TYPE_OFFSET) -+ -+#define autofs_type_trigger(type) \ -+ (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET) -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+#define set_autofs_type_any(type) (type = AUTOFS_TYPE_ANY) -+#define autofs_type_any(type) (type == AUTOFS_TYPE_ANY) - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.24.4-v5-update-20081027.patch b/patches/autofs4-2.6.24.4-v5-update-20081027.patch deleted file mode 100644 index de136eb..0000000 --- a/patches/autofs4-2.6.24.4-v5-update-20081027.patch +++ /dev/null @@ -1,1774 +0,0 @@ -Index: linux-2.6.24.4/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/waitq.c -+++ linux-2.6.24.4/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.24.4/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/expire.c -+++ linux-2.6.24.4/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.24.4/fs/autofs4/root.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/root.c -+++ linux-2.6.24.4/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -568,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -584,8 +482,26 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { - /* - * Mark the dentry incomplete but don't hash it. We do this - * to serialize our inode creation operations (symlink and -@@ -599,39 +515,34 @@ static struct dentry *autofs4_lookup(str - */ - dentry->d_op = &autofs4_root_dentry_operations; - -- dentry->d_fsdata = NULL; -- d_instantiate(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); - /* -- * 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, so just wait for it. -- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear -- * before continuing as revalidate may fail when calling -- * try_to_fill_dentry (returning EAGAIN) if we don't. -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. - */ -- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- dentry = unhashed; -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -650,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -683,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -705,20 +618,31 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -734,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -746,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -778,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -806,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -843,10 +769,20 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -899,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -1000,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.24.4/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.24.4/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.24.4/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.24.4.orig/fs/autofs4/inode.c -+++ linux-2.6.24.4/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -279,7 +283,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -327,14 +331,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -368,7 +373,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.24.4/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.24.4.orig/fs/compat_ioctl.c -+++ linux-2.6.24.4/fs/compat_ioctl.c -@@ -2384,8 +2384,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.24.4/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.24.4.orig/include/linux/auto_fs4.h -+++ linux-2.6.24.4/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.24.4-v5-update-20090903.patch b/patches/autofs4-2.6.24.4-v5-update-20090903.patch new file mode 100644 index 0000000..349c13e --- /dev/null +++ b/patches/autofs4-2.6.24.4-v5-update-20090903.patch @@ -0,0 +1,3505 @@ +--- linux-2.6.24.4.orig/fs/autofs4/waitq.c ++++ linux-2.6.24.4/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.24.4.orig/fs/autofs4/expire.c ++++ linux-2.6.24.4/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.24.4.orig/fs/autofs4/root.c ++++ linux-2.6.24.4/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,50 +177,62 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->mnt, &nd->dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -401,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -414,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -438,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -470,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -495,7 +357,7 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -503,14 +365,66 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -532,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -568,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -584,8 +482,10 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* + * Mark the dentry incomplete but don't hash it. We do this + * to serialize our inode creation operations (symlink and +@@ -599,38 +499,50 @@ static struct dentry *autofs4_lookup(str + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_instantiate(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. +- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear +- * before continuing as revalidate may fail when calling +- * try_to_fill_dentry (returning EAGAIN) if we don't. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -650,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -683,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -705,20 +619,31 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -734,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -746,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -778,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -806,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -843,10 +770,20 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -899,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -1000,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.24.4.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.24.4/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.24.4.orig/fs/autofs4/inode.c ++++ linux-2.6.24.4/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -186,9 +192,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -273,13 +279,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(*type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(*type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(*type); + break; + default: + return 1; +@@ -327,14 +333,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -368,7 +375,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.24.4.orig/fs/compat_ioctl.c ++++ linux-2.6.24.4/fs/compat_ioctl.c +@@ -2384,8 +2384,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.24.4.orig/include/linux/auto_fs4.h ++++ linux-2.6.24.4/include/linux/auto_fs4.h +@@ -23,12 +23,37 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++#define set_autofs_type_indirect(type) (type = AUTOFS_TYPE_INDIRECT) ++#define autofs_type_indirect(type) (type == AUTOFS_TYPE_INDIRECT) ++ ++#define set_autofs_type_direct(type) (type = AUTOFS_TYPE_DIRECT) ++#define autofs_type_direct(type) (type == AUTOFS_TYPE_DIRECT) ++ ++#define set_autofs_type_offset(type) (type = AUTOFS_TYPE_OFFSET) ++#define autofs_type_offset(type) (type == AUTOFS_TYPE_OFFSET) ++ ++#define autofs_type_trigger(type) \ ++ (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET) ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++#define set_autofs_type_any(type) (type = AUTOFS_TYPE_ANY) ++#define autofs_type_any(type) (type == AUTOFS_TYPE_ANY) ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +123,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.24.4/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.24.4.orig/fs/autofs4/Makefile ++++ linux-2.6.24.4/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.24.4/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ inode = nd->dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->dentry); ++ nd->dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->mnt, &nd->dentry)) { ++ ino = autofs4_dentry_ino(nd->dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_release(&nd); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.dentry->d_inode && ++ nd.mnt->mnt_root == nd.dentry) { ++ err = 1; ++ magic = nd.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.dentry); ++ ++ if (nd.mnt->mnt_mountpoint != nd.mnt->mnt_root) { ++ if (follow_down(&nd.mnt, &nd.dentry)) { ++ struct inode *inode = nd.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_release(&nd); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.24.4.orig/fs/autofs4/init.c ++++ linux-2.6.24.4/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.24.4/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.24.4.orig/include/linux/auto_fs.h ++++ linux-2.6.24.4/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.25-dev-ioctl-20081029.patch b/patches/autofs4-2.6.25-dev-ioctl-20081029.patch deleted file mode 100644 index 0779625..0000000 --- a/patches/autofs4-2.6.25-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.25/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.25/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __FUNCTION__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.25/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/inode.c -+++ linux-2.6.25/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -195,9 +197,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -282,13 +284,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -336,7 +338,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -378,7 +380,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.25/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/waitq.c -+++ linux-2.6.25/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.25/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.25/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.25/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/Makefile -+++ linux-2.6.25/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.25/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.25/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ inode = nd->path.dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ ino = autofs4_dentry_ino(nd->path.dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_put(&nd.path); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.path.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.path.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.path.dentry->d_inode && -+ nd.path.mnt->mnt_root == nd.path.dentry) { -+ err = 1; -+ magic = nd.path.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.path.dentry); -+ -+ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { -+ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { -+ struct inode *inode = nd.path.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.25/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/expire.c -+++ linux-2.6.25/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.25/fs/autofs4/init.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/init.c -+++ linux-2.6.25/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.25/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.25/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.25/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.25.orig/include/linux/auto_fs4.h -+++ linux-2.6.25/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.25-v5-update-20081027.patch b/patches/autofs4-2.6.25-v5-update-20081027.patch deleted file mode 100644 index 9ac8c66..0000000 --- a/patches/autofs4-2.6.25-v5-update-20081027.patch +++ /dev/null @@ -1,1776 +0,0 @@ -Index: linux-2.6.25/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/waitq.c -+++ linux-2.6.25/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.25/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/expire.c -+++ linux-2.6.25/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -73,8 +84,8 @@ static int autofs4_mount_busy(struct vfs - status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -333,7 +356,7 @@ static struct dentry *autofs4_expire_ind - /* Can we expire this guy */ - if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } -@@ -343,46 +366,80 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if (expired) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -392,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -408,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -425,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -435,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.25/fs/autofs4/root.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/root.c -+++ linux-2.6.25/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { -- dput(fp_dentry); -- mntput(fp_mnt); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -+ return -ENOENT; - } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -+ spin_unlock(&dcache_lock); - -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -+ int status; - - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); -@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return status; -+ -+ return 0; - } - - /* For autofs direct mounts the follow link triggers the mount */ -@@ -333,51 +177,63 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->path.mnt, &nd->path.dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->path.mnt, -- &nd->path.dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->path.mnt, -+ &nd->path.dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -402,12 +258,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -415,17 +282,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -439,6 +298,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -471,10 +331,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -496,7 +358,7 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -504,14 +366,66 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -533,33 +447,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct inode *inode = dentry->d_inode; -- -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -569,7 +466,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -585,8 +483,26 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { - /* - * Mark the dentry incomplete but don't hash it. We do this - * to serialize our inode creation operations (symlink and -@@ -600,39 +516,34 @@ static struct dentry *autofs4_lookup(str - */ - dentry->d_op = &autofs4_root_dentry_operations; - -- dentry->d_fsdata = NULL; -- d_instantiate(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); - /* -- * 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, so just wait for it. -- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear -- * before continuing as revalidate may fail when calling -- * try_to_fill_dentry (returning EAGAIN) if we don't. -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. - */ -- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- dentry = unhashed; -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -651,9 +562,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -684,7 +597,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -706,20 +619,31 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -735,6 +659,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -747,9 +672,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -779,9 +703,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -807,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -844,10 +770,20 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -900,44 +836,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -1001,11 +899,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.25/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.25/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.25/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.25.orig/fs/autofs4/inode.c -+++ linux-2.6.25/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -284,7 +288,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -332,14 +336,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -373,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.25/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.25.orig/fs/compat_ioctl.c -+++ linux-2.6.25/fs/compat_ioctl.c -@@ -2350,8 +2350,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.25/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.25.orig/include/linux/auto_fs4.h -+++ linux-2.6.25/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.25-v5-update-20090903.patch b/patches/autofs4-2.6.25-v5-update-20090903.patch new file mode 100644 index 0000000..0de06fe --- /dev/null +++ b/patches/autofs4-2.6.25-v5-update-20090903.patch @@ -0,0 +1,3541 @@ +--- linux-2.6.25.orig/fs/autofs4/waitq.c ++++ linux-2.6.25/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -171,7 +194,7 @@ static int autofs4_getpath(struct autofs + for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) + len += tmp->d_name.len + 1; + +- if (--len > NAME_MAX) { ++ if (!len || --len > NAME_MAX) { + spin_unlock(&dcache_lock); + return 0; + } +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.25.orig/fs/autofs4/expire.c ++++ linux-2.6.25/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -73,8 +86,8 @@ static int autofs4_mount_busy(struct vfs + status = 0; + done: + DPRINTK("returning = %d", status); +- mntput(mnt); + dput(dentry); ++ mntput(mnt); + return status; + } + +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -333,7 +358,7 @@ static struct dentry *autofs4_expire_ind + /* Can we expire this guy */ + if (autofs4_can_expire(dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } + goto next; + } +@@ -343,46 +368,80 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; +- break; ++ goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +- break; ++ goto found; + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; + } ++ spin_unlock(&dcache_lock); ++ return NULL; + +- if (expired) { +- DPRINTK("returning %p %.*s", +- expired, (int)expired->d_name.len, expired->d_name.name); +- spin_lock(&dcache_lock); +- list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); +- spin_unlock(&dcache_lock); +- return expired; +- } ++found: ++ DPRINTK("returning %p %.*s", ++ expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ spin_lock(&dcache_lock); ++ list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); ++ return expired; ++} + +- return NULL; ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; + } + + /* Perform an expiry operation */ +@@ -392,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -408,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.25.orig/fs/autofs4/root.c ++++ linux-2.6.25/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,157 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct vfsmount *fp_mnt = mntget(mnt); +- struct dentry *fp_dentry = dget(dentry); +- +- if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { +- dput(fp_dentry); +- mntput(fp_mnt); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; +- } + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- int status = 0; +- +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } ++ int status; + + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); +@@ -291,7 +133,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -318,7 +161,8 @@ static int try_to_fill_dentry(struct den + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- return status; ++ ++ return 0; + } + + /* For autofs direct mounts the follow link triggers the mount */ +@@ -333,51 +177,63 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->path.mnt, &nd->path.dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->path.mnt, +- &nd->path.dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->path.mnt, ++ &nd->path.dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -402,12 +258,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -415,17 +282,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -439,6 +298,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -471,10 +331,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -496,7 +358,7 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -504,14 +366,66 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -533,33 +447,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct inode *inode = dentry->d_inode; +- +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -569,7 +466,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -585,8 +483,10 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* + * Mark the dentry incomplete but don't hash it. We do this + * to serialize our inode creation operations (symlink and +@@ -600,38 +500,50 @@ static struct dentry *autofs4_lookup(str + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_instantiate(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. +- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear +- * before continuing as revalidate may fail when calling +- * try_to_fill_dentry (returning EAGAIN) if we don't. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -651,9 +563,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -684,7 +598,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -706,20 +620,31 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -735,6 +660,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -747,9 +673,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -779,9 +704,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -807,9 +733,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -844,10 +771,20 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -900,44 +837,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -1001,11 +900,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.25.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.25/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.25.orig/fs/autofs4/inode.c ++++ linux-2.6.25/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -191,9 +197,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -278,13 +284,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -332,14 +338,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -373,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.25.orig/fs/compat_ioctl.c ++++ linux-2.6.25/fs/compat_ioctl.c +@@ -2350,8 +2350,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.25.orig/include/linux/auto_fs4.h ++++ linux-2.6.25/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.25/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.25.orig/fs/autofs4/Makefile ++++ linux-2.6.25/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.25/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ inode = nd->path.dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ ino = autofs4_dentry_ino(nd->path.dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_put(&nd.path); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.path.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.path.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.path.dentry->d_inode && ++ nd.path.mnt->mnt_root == nd.path.dentry) { ++ err = 1; ++ magic = nd.path.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.path.dentry); ++ ++ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { ++ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { ++ struct inode *inode = nd.path.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.25.orig/fs/autofs4/init.c ++++ linux-2.6.25/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.25/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.25.orig/include/linux/auto_fs.h ++++ linux-2.6.25/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.26-dev-ioctl-20081029.patch b/patches/autofs4-2.6.26-dev-ioctl-20081029.patch deleted file mode 100644 index ab715da..0000000 --- a/patches/autofs4-2.6.26-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1918 +0,0 @@ -Index: linux-2.6.26/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.26/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __func__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.26/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/inode.c -+++ linux-2.6.26/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -195,9 +197,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -282,13 +284,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -336,7 +338,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -378,7 +380,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.26/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/waitq.c -+++ linux-2.6.26/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.26/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.26/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.26/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/Makefile -+++ linux-2.6.26/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.26/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.26/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,840 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ inode = nd->path.dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ ino = autofs4_dentry_ino(nd->path.dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_put(&nd.path); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.path.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.path.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.path.dentry->d_inode && -+ nd.path.mnt->mnt_root == nd.path.dentry) { -+ err = 1; -+ magic = nd.path.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.path.dentry); -+ -+ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { -+ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { -+ struct inode *inode = nd.path.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.26/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/expire.c -+++ linux-2.6.26/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.26/fs/autofs4/init.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/init.c -+++ linux-2.6.26/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.26/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.26/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.26/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.26.orig/include/linux/auto_fs4.h -+++ linux-2.6.26/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.26-v5-update-20081027.patch b/patches/autofs4-2.6.26-v5-update-20081027.patch deleted file mode 100644 index e05720f..0000000 --- a/patches/autofs4-2.6.26-v5-update-20081027.patch +++ /dev/null @@ -1,1754 +0,0 @@ -Index: linux-2.6.26/fs/autofs4/root.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/root.c -+++ linux-2.6.26/fs/autofs4/root.c -@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); - static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); - static void *autofs4_follow_link(struct dentry *, struct nameidata *); - -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) -+ - const struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - const struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - - const struct inode_operations autofs4_indirect_root_inode_operations = { -@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return dcache_readdir(file, dirent, filldir); --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_path.dentry; -- struct vfsmount *mnt = file->f_path.mnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor; -- int status; -- -- status = dcache_dir_open(inode, file); -- if (status) -- goto out; -- -- cursor = file->private_data; -- cursor->d_fsdata = NULL; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -113,159 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- dcache_dir_close(inode, file); -- status = -EBUSY; -- goto out; -- } -- -- status = -ENOENT; -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty, ret; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ spin_lock(&dcache_lock); -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- ret = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (ret <= 0) { -- if (ret < 0) -- status = ret; -- dcache_dir_close(inode, file); -- goto out; -- } -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct path fp_path = { .dentry = dentry, .mnt = mnt }; -- -- path_get(&fp_path); -- -- if (!autofs4_follow_mount(&fp_path.mnt, &fp_path.dentry)) { -- path_put(&fp_path); -- dcache_dir_close(inode, file); -- goto out; -- } -- -- fp = dentry_open(fp_path.dentry, fp_path.mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- dcache_dir_close(inode, file); -- goto out; -- } -- cursor->d_fsdata = fp; -- } -- return 0; --out: -- return status; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status = 0; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- status = -EBUSY; -- goto out; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- if (!fp) { -- status = -ENOENT; -- goto out; -- } -- filp_close(fp, current->files); -- } --out: -- dcache_dir_close(inode, file); -- return status; --} -- --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) --{ -- struct dentry *dentry = file->f_path.dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- struct dentry *cursor = file->private_data; -- int status; -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = cursor->d_fsdata; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - out: -- return dcache_readdir(file, dirent, filldir); -+ return dcache_dir_open(inode, file); - } - - static int try_to_fill_dentry(struct dentry *dentry, int flags) - { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -- struct dentry *new; - int status; - -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- /* -- * If the directory still exists the mount request must -- * continue otherwise it can't be followed at the right -- * time during the walk. -- */ -- status = d_invalidate(dentry); -- if (status != -EBUSY) -- return -EAGAIN; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -292,7 +132,8 @@ static int try_to_fill_dentry(struct den - return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -320,26 +161,6 @@ static int try_to_fill_dentry(struct den - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); - -- /* -- * The dentry that is passed in from lookup may not be the one -- * we end up using, as mkdir can create a new one. If this -- * happens, and another process tries the lookup at the same time, -- * it will set the PENDING flag on this new dentry, but add itself -- * to our waitq. Then, if after the lookup succeeds, the first -- * process that requested the mount performs another lookup of the -- * same directory, it will show up as still pending! So, we need -- * to redo the lookup here and clear pending on that dentry. -- */ -- if (d_unhashed(dentry)) { -- new = d_lookup(dentry->d_parent, &dentry->d_name); -- if (new) { -- spin_lock(&new->d_lock); -- new->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&new->d_lock); -- dput(new); -- } -- } -- - return 0; - } - -@@ -355,51 +176,63 @@ static void *autofs4_follow_link(struct - DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", - dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, - nd->flags); -- -- /* If it's our master or we shouldn't trigger a mount we're done */ -- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); -- if (oz_mode || !lookup_type) -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->path.mnt, &nd->path.dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); - goto done; -+ } - -- /* If an expire request is pending wait for it. */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for active request %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); - -- DPRINTK("request done status=%d", status); -- } -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; - - /* -- * If the dentry contains directories then it is an -- * autofs multi-mount with no root mount offset. So -- * don't try to mount it again. -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); - if (status) - goto out_error; - -- /* -- * The mount succeeded but if there is no root mount -- * it must be an autofs multi-mount with no root offset -- * so we don't need to follow the mount. -- */ -- if (d_mountpoint(dentry)) { -- if (!autofs4_follow_mount(&nd->path.mnt, -- &nd->path.dentry)) { -- status = -ENOENT; -- goto out_error; -- } -- } -- -- goto done; -+ goto follow; - } - spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->path.mnt, -+ &nd->path.dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } - - done: - return NULL; -@@ -424,12 +257,23 @@ static int autofs4_revalidate(struct den - int status = 1; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { - /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ - if (oz_mode) - return 1; - - /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* - * A zero status is success otherwise we have a - * negative error code. - */ -@@ -437,17 +281,9 @@ static int autofs4_revalidate(struct den - if (status == 0) - return 1; - -- /* -- * A status of EAGAIN here means that the dentry has gone -- * away while waiting for an expire to complete. If we are -- * racing with expire lookup will wait for it so this must -- * be a revalidate and we need to send it to lookup. -- */ -- if (status == -EAGAIN) -- return 0; -- - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -@@ -461,6 +297,7 @@ static int autofs4_revalidate(struct den - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -+ - /* The daemon never causes a mount to trigger */ - if (oz_mode) - return 1; -@@ -493,10 +330,12 @@ void autofs4_dentry_release(struct dentr - struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); - - if (sbi) { -- spin_lock(&sbi->rehash_lock); -- if (!list_empty(&inf->rehash)) -- list_del(&inf->rehash); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); - } - - inf->dentry = NULL; -@@ -518,7 +357,59 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) - { - unsigned int len = name->len; - unsigned int hash = name->hash; -@@ -526,14 +417,14 @@ static struct dentry *autofs4_lookup_unh - struct list_head *p, *head; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- head = &sbi->rehash_list; -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; - list_for_each(p, head) { - struct autofs_info *ino; - struct dentry *dentry; - struct qstr *qstr; - -- ino = list_entry(p, struct autofs_info, rehash); -+ ino = list_entry(p, struct autofs_info, expiring); - dentry = ino->dentry; - - spin_lock(&dentry->d_lock); -@@ -555,33 +446,16 @@ static struct dentry *autofs4_lookup_unh - goto next; - - if (d_unhashed(dentry)) { -- struct inode *inode = dentry->d_inode; -- -- ino = autofs4_dentry_ino(dentry); -- list_del_init(&ino->rehash); - dget(dentry); -- /* -- * Make the rehashed dentry negative so the VFS -- * behaves as it should. -- */ -- if (inode) { -- dentry->d_inode = NULL; -- list_del_init(&dentry->d_alias); -- spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -- spin_unlock(&dcache_lock); -- iput(inode); -- return dentry; -- } - spin_unlock(&dentry->d_lock); -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - return dentry; - } - next: - spin_unlock(&dentry->d_lock); - } -- spin_unlock(&sbi->rehash_lock); -+ spin_unlock(&sbi->lookup_lock); - spin_unlock(&dcache_lock); - - return NULL; -@@ -591,7 +465,8 @@ next: - static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -- struct dentry *unhashed; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -607,8 +482,26 @@ static struct dentry *autofs4_lookup(str - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); - -- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -- if (!unhashed) { -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { - /* - * Mark the dentry incomplete but don't hash it. We do this - * to serialize our inode creation operations (symlink and -@@ -622,39 +515,34 @@ static struct dentry *autofs4_lookup(str - */ - dentry->d_op = &autofs4_root_dentry_operations; - -- dentry->d_fsdata = NULL; -- d_instantiate(dentry, NULL); -- } else { -- struct autofs_info *ino = autofs4_dentry_ino(unhashed); -- DPRINTK("rehash %p with %p", dentry, unhashed); - /* -- * 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, so just wait for it. -- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear -- * before continuing as revalidate may fail when calling -- * try_to_fill_dentry (returning EAGAIN) if we don't. -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. - */ -- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("wait for incomplete expire %p name=%.*s", -- unhashed, unhashed->d_name.len, -- unhashed->d_name.name); -- autofs4_wait(sbi, unhashed, NFY_NONE); -- DPRINTK("request completed"); -- } -- dentry = unhashed; -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); - } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- mutex_unlock(&dir->i_mutex); -- (dentry->d_op->d_revalidate)(dentry, nd); -- mutex_lock(&dir->i_mutex); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ mutex_unlock(&dir->i_mutex); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ mutex_lock(&dir->i_mutex); -+ } - } - - /* -@@ -673,9 +561,11 @@ static struct dentry *autofs4_lookup(str - return ERR_PTR(-ERESTARTNOINTR); - } - } -- spin_lock(&dentry->d_lock); -- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -- spin_unlock(&dentry->d_lock); -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* -@@ -706,7 +596,7 @@ static struct dentry *autofs4_lookup(str - } - - if (unhashed) -- return dentry; -+ return unhashed; - - return NULL; - } -@@ -728,20 +618,31 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -757,6 +658,7 @@ static int autofs4_dir_symlink(struct in - atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -769,9 +671,8 @@ static int autofs4_dir_symlink(struct in - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want - * this, because the unlink is probably the result of an expire. -- * We simply d_drop it and add it to a rehash candidates list in the -- * super block, which allows the dentry lookup to reuse it retaining -- * the flags, such as expire in progress, in case we're racing with expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -801,9 +702,10 @@ static int autofs4_dir_unlink(struct ino - dir->i_mtime = CURRENT_TIME; - - spin_lock(&dcache_lock); -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -829,9 +731,10 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -- spin_lock(&sbi->rehash_lock); -- list_add(&ino->rehash, &sbi->rehash_list); -- spin_unlock(&sbi->rehash_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -866,10 +769,20 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } - d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) -@@ -922,44 +835,6 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if (status) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) -@@ -1023,11 +898,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_path.mnt, p); - -Index: linux-2.6.26/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.26/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -52,7 +54,10 @@ struct autofs_info { - - int flags; - -- struct list_head rehash; -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; - - struct autofs_sb_info *sbi; - unsigned long last_used; -@@ -68,15 +73,14 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- unsigned int hash; -- unsigned int len; -- char *name; -+ struct qstr name; - u32 dev; - u64 ino; - uid_t uid; -@@ -85,15 +89,11 @@ struct autofs_wait_queue { - pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -@@ -112,8 +112,9 @@ struct autofs_sb_info { - struct mutex wq_mutex; - spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -- spinlock_t rehash_lock; -- struct list_head rehash_list; -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -138,18 +139,14 @@ static inline int autofs4_oz_mode(struct - static inline int autofs4_ispending(struct dentry *dentry) - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); -- int pending = 0; - - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) - return 1; - -- if (inf) { -- spin_lock(&inf->sbi->fs_lock); -- pending = inf->flags & AUTOFS_INF_EXPIRING; -- spin_unlock(&inf->sbi->fs_lock); -- } -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; - -- return pending; -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -164,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -Index: linux-2.6.26/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/inode.c -+++ linux-2.6.26/fs/autofs4/inode.c -@@ -24,8 +24,10 @@ - - static void ino_lnkfree(struct autofs_info *ino) - { -- kfree(ino->u.symlink); -- ino->u.symlink = NULL; -+ if (ino->u.symlink) { -+ kfree(ino->u.symlink); -+ ino->u.symlink = NULL; -+ } - } - - struct autofs_info *autofs4_init_ino(struct autofs_info *ino, -@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -- -- INIT_LIST_HEAD(&ino->rehash); -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; -- atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -159,8 +163,8 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- if (!sbi->catatonic) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); -@@ -284,7 +288,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -332,14 +336,15 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -- spin_lock_init(&sbi->rehash_lock); -- INIT_LIST_HEAD(&sbi->rehash_list); -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -373,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.26/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/waitq.c -+++ linux-2.6.26/fs/autofs4/waitq.c -@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ mutex_lock(&sbi->wq_mutex); -+ if (sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; -@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof - while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; -+ sbi->pipefd = -1; -+ mutex_unlock(&sbi->wq_mutex); - } - - static int autofs4_write(struct file *file, const void *addr, int bytes) -@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct - union autofs_packet_union v4_pkt; - union autofs_v5_packet_union v5_pkt; - } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - -@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; - break; - } - case autofs_ptype_expire_multi: -@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; - break; - } - /* -@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct - pktsz = sizeof(*packet); - - packet->wait_queue_token = wq->wait_queue_token; -- packet->len = wq->len; -- memcpy(packet->name, wq->name, wq->len); -- packet->name[wq->len] = '\0'; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; - packet->dev = wq->dev; - packet->ino = wq->ino; - packet->uid = wq->uid; -@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ mutex_unlock(&sbi->wq_mutex); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs - } - - static struct autofs_wait_queue * --autofs4_find_wait(struct autofs_sb_info *sbi, -- char *name, unsigned int hash, unsigned int len) -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) - { - struct autofs_wait_queue *wq; - - for (wq = sbi->queues; wq; wq = wq->next) { -- if (wq->hash == hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && -+ !memcmp(wq->name.name, qstr->name, qstr->len)) - break; - } - return wq; - } - --int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -- enum autofs_notify notify) -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) - { -- struct autofs_info *ino; - struct autofs_wait_queue *wq; -- char *name; -- unsigned int len = 0; -- unsigned int hash = 0; -- int status, type; -- -- /* In catatonic mode, we don't wait for nobody */ -- if (sbi->catatonic) -- return -ENOENT; -- -- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -- if (!name) -- return -ENOMEM; -+ struct autofs_info *ino; - -- /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -- len = sprintf(name, "%p", dentry); -- else { -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -- } -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- hash = full_name_hash(name, len); - -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -- return -EINTR; -- } -+ *wait = NULL; - -- wq = autofs4_find_wait(sbi, name, hash, len); -+ /* If we don't yet have any info this is a new request */ - ino = autofs4_dentry_ino(dentry); -- if (!wq && ino && notify == NFY_NONE) { -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { - /* - * Either we've betean the pending expire to post it's - * wait or it finished while we waited on the mutex. -@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * - while (ino->flags & AUTOFS_INF_EXPIRING) { - mutex_unlock(&sbi->wq_mutex); - schedule_timeout_interruptible(HZ/10); -- if (mutex_lock_interruptible(&sbi->wq_mutex)) { -- kfree(name); -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) - return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; - } -- wq = autofs4_find_wait(sbi, name, hash, len); -- if (wq) -- break; - } - - /* -@@ -267,18 +288,96 @@ int autofs4_wait(struct autofs_sb_info * - * cases where we wait on NFY_NONE neither depend on the - * return status of the wait. - */ -- if (!wq) { -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the mutex ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_mutex. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, -+ enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct qstr qstr; -+ char *name; -+ int status, ret, type; -+ -+ /* In catatonic mode, we don't wait for nobody */ -+ if (sbi->catatonic) -+ return -ENOENT; -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ -+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); -+ if (!name) -+ return -ENOMEM; -+ -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { - kfree(name); -- mutex_unlock(&sbi->wq_mutex); -- return 0; -+ return -ENOENT; - } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); -+ -+ if (mutex_lock_interruptible(&sbi->wq_mutex)) { -+ kfree(qstr.name); -+ return -EINTR; -+ } -+ -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ mutex_unlock(&sbi->wq_mutex); -+ kfree(qstr.name); -+ return ret; -+ } - - if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if (!wq) { -- kfree(name); -+ kfree(qstr.name); - mutex_unlock(&sbi->wq_mutex); - return -ENOMEM; - } -@@ -289,9 +388,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); - wq->dev = autofs4_get_dev(sbi); - wq->ino = autofs4_get_ino(sbi); - wq->uid = current->uid; -@@ -299,7 +396,7 @@ int autofs4_wait(struct autofs_sb_info * - wq->pid = current->pid; - wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - mutex_unlock(&sbi->wq_mutex); - - if (sbi->version < 5) { -@@ -309,38 +406,35 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } - - DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - mutex_unlock(&sbi->wq_mutex); -- kfree(name); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- /* wq->name is NULL if and only if the lock is already released */ -- -- if (sbi->catatonic) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- kfree(wq->name); -- wq->name = NULL; -- } -- -- if (wq->name) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -351,7 +445,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -364,8 +458,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ mutex_lock(&sbi->wq_mutex); -+ if (!--wq->wait_ctr) - kfree(wq); -+ mutex_unlock(&sbi->wq_mutex); - - return status; - } -@@ -387,16 +483,13 @@ int autofs4_wait_release(struct autofs_s - } - - *wql = wq->next; /* Unlink from chain */ -- mutex_unlock(&sbi->wq_mutex); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ mutex_unlock(&sbi->wq_mutex); - - return 0; - } -Index: linux-2.6.26/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.26.orig/fs/autofs4/expire.c -+++ linux-2.6.26/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -259,13 +270,15 @@ static struct dentry *autofs4_expire_dir - now = jiffies; - timeout = sbi->exp_timeout; - -- /* Lock the tree as we must expire as a whole */ - spin_lock(&sbi->fs_lock); - if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { - struct autofs_info *ino = autofs4_dentry_ino(root); -- -- /* Set this flag early to catch sys_chdir and the like */ -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } - ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); - spin_unlock(&sbi->fs_lock); - return root; - } -@@ -292,6 +305,8 @@ static struct dentry *autofs4_expire_ind - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if (!root) - return NULL; -@@ -316,6 +331,9 @@ static struct dentry *autofs4_expire_ind - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ - /* - * Case 1: (i) indirect mount or top level pseudo direct mount - * (autofs-4.1). -@@ -326,6 +344,11 @@ static struct dentry *autofs4_expire_ind - 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 (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - /* Can we umount this guy */ - if (autofs4_mount_busy(mnt, dentry)) - goto next; -@@ -343,23 +366,25 @@ static struct dentry *autofs4_expire_ind - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- /* Lock the tree as we must expire as a whole */ -- spin_lock(&sbi->fs_lock); -- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -- struct autofs_info *inf = autofs4_dentry_ino(dentry); -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; - -- /* Set this flag early to catch sys_chdir and the like */ -- inf->flags |= AUTOFS_INF_EXPIRING; -- spin_unlock(&sbi->fs_lock); -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - expired = dentry; - goto found; - } -- spin_unlock(&sbi->fs_lock); - /* - * Case 3: pseudo direct mount, expire individual leaves - * (autofs-4.1). - */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -@@ -367,6 +392,7 @@ static struct dentry *autofs4_expire_ind - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; -@@ -377,12 +403,45 @@ next: - found: - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - spin_lock(&dcache_lock); - list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); - spin_unlock(&dcache_lock); - return expired; - } - -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; -+} -+ - /* Perform an expiry operation */ - int autofs4_expire_run(struct super_block *sb, - struct vfsmount *mnt, -@@ -390,7 +449,9 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - -@@ -406,9 +467,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -423,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -@@ -433,9 +500,16 @@ int autofs4_expire_multi(struct super_bl - - /* This is synchronous because it makes the daemon a - little easier */ -- ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } - ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } - -Index: linux-2.6.26/fs/compat_ioctl.c -=================================================================== ---- linux-2.6.26.orig/fs/compat_ioctl.c -+++ linux-2.6.26/fs/compat_ioctl.c -@@ -2350,8 +2350,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* Raw devices */ - COMPATIBLE_IOCTL(RAW_SETBIND) -Index: linux-2.6.26/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.26.orig/include/linux/auto_fs4.h -+++ linux-2.6.26/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, -@@ -98,8 +103,6 @@ union autofs_v5_packet_union { - #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - diff --git a/patches/autofs4-2.6.26-v5-update-20090903.patch b/patches/autofs4-2.6.26-v5-update-20090903.patch new file mode 100644 index 0000000..d62ebb1 --- /dev/null +++ b/patches/autofs4-2.6.26-v5-update-20090903.patch @@ -0,0 +1,3519 @@ +--- linux-2.6.26.orig/fs/autofs4/root.c ++++ linux-2.6.26/fs/autofs4/root.c +@@ -25,25 +25,25 @@ static int autofs4_dir_rmdir(struct inod + static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + static int autofs4_dir_open(struct inode *inode, struct file *file); +-static int autofs4_dir_close(struct inode *inode, struct file *file); +-static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); +-static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); + static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); + static void *autofs4_follow_link(struct dentry *, struct nameidata *); + ++#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ++#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) ++ + const struct file_operations autofs4_root_operations = { + .open = dcache_dir_open, + .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_root_readdir, ++ .readdir = dcache_readdir, + .ioctl = autofs4_root_ioctl, + }; + + const struct file_operations autofs4_dir_operations = { + .open = autofs4_dir_open, +- .release = autofs4_dir_close, ++ .release = dcache_dir_close, + .read = generic_read_dir, +- .readdir = autofs4_dir_readdir, ++ .readdir = dcache_readdir, + }; + + const struct inode_operations autofs4_indirect_root_inode_operations = { +@@ -70,42 +70,10 @@ const struct inode_operations autofs4_di + .rmdir = autofs4_dir_rmdir, + }; + +-static int autofs4_root_readdir(struct file *file, void *dirent, +- filldir_t filldir) +-{ +- struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); +- int oz_mode = autofs4_oz_mode(sbi); +- +- DPRINTK("called, filp->f_pos = %lld", file->f_pos); +- +- /* +- * Don't set reghost flag if: +- * 1) f_pos is larger than zero -- we've already been here. +- * 2) we haven't even enabled reghosting in the 1st place. +- * 3) this is the daemon doing a readdir +- */ +- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) +- sbi->needs_reghost = 1; +- +- DPRINTK("needs_reghost = %d", sbi->needs_reghost); +- +- return dcache_readdir(file, dirent, filldir); +-} +- + static int autofs4_dir_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_path.dentry; +- struct vfsmount *mnt = file->f_path.mnt; + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor; +- int status; +- +- status = dcache_dir_open(inode, file); +- if (status) +- goto out; +- +- cursor = file->private_data; +- cursor->d_fsdata = NULL; + + DPRINTK("file=%p dentry=%p %.*s", + file, dentry, dentry->d_name.len, dentry->d_name.name); +@@ -113,159 +81,31 @@ static int autofs4_dir_open(struct inode + if (autofs4_oz_mode(sbi)) + goto out; + +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- dcache_dir_close(inode, file); +- status = -EBUSY; +- goto out; +- } +- +- status = -ENOENT; +- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { +- struct nameidata nd; +- int empty, ret; +- +- /* In case there are stale directory dentrys from a failed mount */ +- spin_lock(&dcache_lock); +- empty = list_empty(&dentry->d_subdirs); ++ /* ++ * An empty directory in an autofs file system is always a ++ * mount point. The daemon must have failed to mount this ++ * during lookup so it doesn't exist. This can happen, for ++ * example, if user space returns an incorrect status for a ++ * mount request. Otherwise we're doing a readdir on the ++ * autofs file system so just let the libfs routines handle ++ * it. ++ */ ++ spin_lock(&dcache_lock); ++ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { + spin_unlock(&dcache_lock); +- +- if (!empty) +- d_invalidate(dentry); +- +- nd.flags = LOOKUP_DIRECTORY; +- ret = (dentry->d_op->d_revalidate)(dentry, &nd); +- +- if (ret <= 0) { +- if (ret < 0) +- status = ret; +- dcache_dir_close(inode, file); +- goto out; +- } +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = NULL; +- struct path fp_path = { .dentry = dentry, .mnt = mnt }; +- +- path_get(&fp_path); +- +- if (!autofs4_follow_mount(&fp_path.mnt, &fp_path.dentry)) { +- path_put(&fp_path); +- dcache_dir_close(inode, file); +- goto out; +- } +- +- fp = dentry_open(fp_path.dentry, fp_path.mnt, file->f_flags); +- status = PTR_ERR(fp); +- if (IS_ERR(fp)) { +- dcache_dir_close(inode, file); +- goto out; +- } +- cursor->d_fsdata = fp; +- } +- return 0; +-out: +- return status; +-} +- +-static int autofs4_dir_close(struct inode *inode, struct file *file) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status = 0; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- status = -EBUSY; +- goto out; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- if (!fp) { +- status = -ENOENT; +- goto out; +- } +- filp_close(fp, current->files); +- } +-out: +- dcache_dir_close(inode, file); +- return status; +-} +- +-static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) +-{ +- struct dentry *dentry = file->f_path.dentry; +- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); +- struct dentry *cursor = file->private_data; +- int status; +- +- DPRINTK("file=%p dentry=%p %.*s", +- file, dentry, dentry->d_name.len, dentry->d_name.name); +- +- if (autofs4_oz_mode(sbi)) +- goto out; +- +- if (autofs4_ispending(dentry)) { +- DPRINTK("dentry busy"); +- return -EBUSY; +- } +- +- if (d_mountpoint(dentry)) { +- struct file *fp = cursor->d_fsdata; +- +- if (!fp) +- return -ENOENT; +- +- if (!fp->f_op || !fp->f_op->readdir) +- goto out; +- +- status = vfs_readdir(fp, filldir, dirent); +- file->f_pos = fp->f_pos; +- if (status) +- autofs4_copy_atime(file, fp); +- return status; ++ return -ENOENT; + } ++ spin_unlock(&dcache_lock); + out: +- return dcache_readdir(file, dirent, filldir); ++ return dcache_dir_open(inode, file); + } + + static int try_to_fill_dentry(struct dentry *dentry, int flags) + { + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); +- struct dentry *new; + int status; + +- /* Block on any pending expiry here; invalidate the dentry +- when expiration is done to trigger mount request with a new +- dentry */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for expire %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); +- +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("expire done status=%d", status); +- +- /* +- * If the directory still exists the mount request must +- * continue otherwise it can't be followed at the right +- * time during the walk. +- */ +- status = d_invalidate(dentry); +- if (status != -EBUSY) +- return -EAGAIN; +- } +- + DPRINTK("dentry=%p %.*s ino=%p", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); + +@@ -292,7 +132,8 @@ static int try_to_fill_dentry(struct den + return status; + } + /* Trigger mount for path component or follow link */ +- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || ++ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || + current->link_count) { + DPRINTK("waiting for mount name=%.*s", + dentry->d_name.len, dentry->d_name.name); +@@ -320,26 +161,6 @@ static int try_to_fill_dentry(struct den + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); + +- /* +- * The dentry that is passed in from lookup may not be the one +- * we end up using, as mkdir can create a new one. If this +- * happens, and another process tries the lookup at the same time, +- * it will set the PENDING flag on this new dentry, but add itself +- * to our waitq. Then, if after the lookup succeeds, the first +- * process that requested the mount performs another lookup of the +- * same directory, it will show up as still pending! So, we need +- * to redo the lookup here and clear pending on that dentry. +- */ +- if (d_unhashed(dentry)) { +- new = d_lookup(dentry->d_parent, &dentry->d_name); +- if (new) { +- spin_lock(&new->d_lock); +- new->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&new->d_lock); +- dput(new); +- } +- } +- + return 0; + } + +@@ -355,51 +176,63 @@ static void *autofs4_follow_link(struct + DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", + dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, + nd->flags); +- +- /* If it's our master or we shouldn't trigger a mount we're done */ +- lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY); +- if (oz_mode || !lookup_type) ++ /* ++ * For an expire of a covered direct or offset mount we need ++ * to beeak out of follow_down() at the autofs mount trigger ++ * (d_mounted--), so we can see the expiring flag, and manage ++ * the blocking and following here until the expire is completed. ++ */ ++ if (oz_mode) { ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ /* Follow down to our covering mount. */ ++ if (!follow_down(&nd->path.mnt, &nd->path.dentry)) ++ goto done; ++ goto follow; ++ } ++ spin_unlock(&sbi->fs_lock); + goto done; ++ } + +- /* If an expire request is pending wait for it. */ +- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("waiting for active request %p name=%.*s", +- dentry, dentry->d_name.len, dentry->d_name.name); ++ /* If an expire request is pending everyone must wait. */ ++ autofs4_expire_wait(dentry); + +- status = autofs4_wait(sbi, dentry, NFY_NONE); +- +- DPRINTK("request done status=%d", status); +- } ++ /* We trigger a mount for almost all flags */ ++ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); ++ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) ++ goto follow; + + /* +- * If the dentry contains directories then it is an +- * autofs multi-mount with no root mount offset. So +- * don't try to mount it again. ++ * If the dentry contains directories then it is an autofs ++ * multi-mount with no root mount offset. So don't try to ++ * mount it again. + */ + spin_lock(&dcache_lock); +- if (!d_mountpoint(dentry) && __simple_empty(dentry)) { ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || ++ (!d_mountpoint(dentry) && __simple_empty(dentry))) { + spin_unlock(&dcache_lock); + + status = try_to_fill_dentry(dentry, 0); + if (status) + goto out_error; + +- /* +- * The mount succeeded but if there is no root mount +- * it must be an autofs multi-mount with no root offset +- * so we don't need to follow the mount. +- */ +- if (d_mountpoint(dentry)) { +- if (!autofs4_follow_mount(&nd->path.mnt, +- &nd->path.dentry)) { +- status = -ENOENT; +- goto out_error; +- } +- } +- +- goto done; ++ goto follow; + } + spin_unlock(&dcache_lock); ++follow: ++ /* ++ * If there is no root mount it must be an autofs ++ * multi-mount with no root offset so we don't need ++ * to follow it. ++ */ ++ if (d_mountpoint(dentry)) { ++ if (!autofs4_follow_mount(&nd->path.mnt, ++ &nd->path.dentry)) { ++ status = -ENOENT; ++ goto out_error; ++ } ++ } + + done: + return NULL; +@@ -424,12 +257,23 @@ static int autofs4_revalidate(struct den + int status = 1; + + /* Pending dentry */ ++ spin_lock(&sbi->fs_lock); + if (autofs4_ispending(dentry)) { + /* The daemon never causes a mount to trigger */ ++ spin_unlock(&sbi->fs_lock); ++ + if (oz_mode) + return 1; + + /* ++ * If the directory has gone away due to an expire ++ * we have been called as ->d_revalidate() and so ++ * we need to return false and proceed to ->lookup(). ++ */ ++ if (autofs4_expire_wait(dentry) == -EAGAIN) ++ return 0; ++ ++ /* + * A zero status is success otherwise we have a + * negative error code. + */ +@@ -437,17 +281,9 @@ static int autofs4_revalidate(struct den + if (status == 0) + return 1; + +- /* +- * A status of EAGAIN here means that the dentry has gone +- * away while waiting for an expire to complete. If we are +- * racing with expire lookup will wait for it so this must +- * be a revalidate and we need to send it to lookup. +- */ +- if (status == -EAGAIN) +- return 0; +- + return status; + } ++ spin_unlock(&sbi->fs_lock); + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) +@@ -461,6 +297,7 @@ static int autofs4_revalidate(struct den + DPRINTK("dentry=%p %.*s, emptydir", + dentry, dentry->d_name.len, dentry->d_name.name); + spin_unlock(&dcache_lock); ++ + /* The daemon never causes a mount to trigger */ + if (oz_mode) + return 1; +@@ -493,10 +330,12 @@ void autofs4_dentry_release(struct dentr + struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); + + if (sbi) { +- spin_lock(&sbi->rehash_lock); +- if (!list_empty(&inf->rehash)) +- list_del(&inf->rehash); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&inf->active)) ++ list_del(&inf->active); ++ if (!list_empty(&inf->expiring)) ++ list_del(&inf->expiring); ++ spin_unlock(&sbi->lookup_lock); + } + + inf->dentry = NULL; +@@ -518,7 +357,59 @@ static struct dentry_operations autofs4_ + .d_release = autofs4_dentry_release, + }; + +-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) ++{ ++ unsigned int len = name->len; ++ unsigned int hash = name->hash; ++ const unsigned char *str = name->name; ++ struct list_head *p, *head; ++ ++ spin_lock(&dcache_lock); ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->active_list; ++ list_for_each(p, head) { ++ struct autofs_info *ino; ++ struct dentry *dentry; ++ struct qstr *qstr; ++ ++ ino = list_entry(p, struct autofs_info, active); ++ dentry = ino->dentry; ++ ++ spin_lock(&dentry->d_lock); ++ ++ /* Already gone? */ ++ if (atomic_read(&dentry->d_count) == 0) ++ goto next; ++ ++ qstr = &dentry->d_name; ++ ++ if (dentry->d_name.hash != hash) ++ goto next; ++ if (dentry->d_parent != parent) ++ goto next; ++ ++ if (qstr->len != len) ++ goto next; ++ if (memcmp(qstr->name, str, len)) ++ goto next; ++ ++ if (d_unhashed(dentry)) { ++ dget(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ return dentry; ++ } ++next: ++ spin_unlock(&dentry->d_lock); ++ } ++ spin_unlock(&sbi->lookup_lock); ++ spin_unlock(&dcache_lock); ++ ++ return NULL; ++} ++ ++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) + { + unsigned int len = name->len; + unsigned int hash = name->hash; +@@ -526,14 +417,14 @@ static struct dentry *autofs4_lookup_unh + struct list_head *p, *head; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- head = &sbi->rehash_list; ++ spin_lock(&sbi->lookup_lock); ++ head = &sbi->expiring_list; + list_for_each(p, head) { + struct autofs_info *ino; + struct dentry *dentry; + struct qstr *qstr; + +- ino = list_entry(p, struct autofs_info, rehash); ++ ino = list_entry(p, struct autofs_info, expiring); + dentry = ino->dentry; + + spin_lock(&dentry->d_lock); +@@ -555,33 +446,16 @@ static struct dentry *autofs4_lookup_unh + goto next; + + if (d_unhashed(dentry)) { +- struct inode *inode = dentry->d_inode; +- +- ino = autofs4_dentry_ino(dentry); +- list_del_init(&ino->rehash); + dget(dentry); +- /* +- * Make the rehashed dentry negative so the VFS +- * behaves as it should. +- */ +- if (inode) { +- dentry->d_inode = NULL; +- list_del_init(&dentry->d_alias); +- spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); +- spin_unlock(&dcache_lock); +- iput(inode); +- return dentry; +- } + spin_unlock(&dentry->d_lock); +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + return dentry; + } + next: + spin_unlock(&dentry->d_lock); + } +- spin_unlock(&sbi->rehash_lock); ++ spin_unlock(&sbi->lookup_lock); + spin_unlock(&dcache_lock); + + return NULL; +@@ -591,7 +465,8 @@ next: + static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + struct autofs_sb_info *sbi; +- struct dentry *unhashed; ++ struct autofs_info *ino; ++ struct dentry *expiring, *unhashed; + int oz_mode; + + DPRINTK("name = %.*s", +@@ -607,8 +482,10 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); +- if (!unhashed) { ++ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); ++ if (unhashed) ++ dentry = unhashed; ++ else { + /* + * Mark the dentry incomplete but don't hash it. We do this + * to serialize our inode creation operations (symlink and +@@ -622,38 +499,50 @@ static struct dentry *autofs4_lookup(str + */ + dentry->d_op = &autofs4_root_dentry_operations; + +- dentry->d_fsdata = NULL; +- d_instantiate(dentry, NULL); +- } else { +- struct autofs_info *ino = autofs4_dentry_ino(unhashed); +- DPRINTK("rehash %p with %p", dentry, unhashed); + /* +- * 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, so just wait for it. +- * We need to ensure the AUTOFS_INF_EXPIRING flag is clear +- * before continuing as revalidate may fail when calling +- * try_to_fill_dentry (returning EAGAIN) if we don't. ++ * And we need to ensure that the same dentry is used for ++ * all following lookup calls until it is hashed so that ++ * the dentry flags are persistent throughout the request. + */ +- while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { +- DPRINTK("wait for incomplete expire %p name=%.*s", +- unhashed, unhashed->d_name.len, +- unhashed->d_name.name); +- autofs4_wait(sbi, unhashed, NFY_NONE); +- DPRINTK("request completed"); +- } +- dentry = unhashed; ++ ino = autofs4_init_ino(NULL, sbi, 0555); ++ if (!ino) ++ return ERR_PTR(-ENOMEM); ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dentry; ++ ++ spin_lock(&sbi->lookup_lock); ++ list_add(&ino->active, &sbi->active_list); ++ spin_unlock(&sbi->lookup_lock); ++ ++ d_instantiate(dentry, NULL); + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- } +- +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); +- (dentry->d_op->d_revalidate)(dentry, nd); ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ (dentry->d_op->d_revalidate)(dentry, nd); + mutex_lock(&dir->i_mutex); + } + +@@ -673,9 +562,11 @@ static struct dentry *autofs4_lookup(str + return ERR_PTR(-ERESTARTNOINTR); + } + } +- spin_lock(&dentry->d_lock); +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- spin_unlock(&dentry->d_lock); ++ if (!oz_mode) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ spin_unlock(&dentry->d_lock); ++ } + } + + /* +@@ -706,7 +597,7 @@ static struct dentry *autofs4_lookup(str + } + + if (unhashed) +- return dentry; ++ return unhashed; + + return NULL; + } +@@ -728,20 +619,31 @@ static int autofs4_dir_symlink(struct in + return -EACCES; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; + +- ino->size = strlen(symname); +- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + +- if (cp == NULL) { +- kfree(ino); +- return -ENOSPC; ++ ino->size = strlen(symname); ++ cp = kmalloc(ino->size + 1, GFP_KERNEL); ++ if (!cp) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; + } + + strcpy(cp, symname); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ kfree(cp); ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -757,6 +659,7 @@ static int autofs4_dir_symlink(struct in + atomic_inc(&p_ino->count); + ino->inode = inode; + ++ ino->u.symlink = cp; + dir->i_mtime = CURRENT_TIME; + + return 0; +@@ -769,9 +672,8 @@ static int autofs4_dir_symlink(struct in + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because the unlink is probably the result of an expire. +- * We simply d_drop it and add it to a rehash candidates list in the +- * super block, which allows the dentry lookup to reuse it retaining +- * the flags, such as expire in progress, in case we're racing with expire. ++ * We simply d_drop it and add it to a expiring list in the super block, ++ * which allows the dentry lookup to check for an incomplete expire. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. +@@ -801,9 +703,10 @@ static int autofs4_dir_unlink(struct ino + dir->i_mtime = CURRENT_TIME; + + spin_lock(&dcache_lock); +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -829,9 +732,10 @@ static int autofs4_dir_rmdir(struct inod + spin_unlock(&dcache_lock); + return -ENOTEMPTY; + } +- spin_lock(&sbi->rehash_lock); +- list_add(&ino->rehash, &sbi->rehash_list); +- spin_unlock(&sbi->rehash_lock); ++ spin_lock(&sbi->lookup_lock); ++ if (list_empty(&ino->expiring)) ++ list_add(&ino->expiring, &sbi->expiring_list); ++ spin_unlock(&sbi->lookup_lock); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +@@ -866,10 +770,20 @@ static int autofs4_dir_mkdir(struct inod + dentry, dentry->d_name.len, dentry->d_name.name); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); +- if (ino == NULL) +- return -ENOSPC; ++ if (!ino) ++ return -ENOMEM; ++ ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->active)) ++ list_del_init(&ino->active); ++ spin_unlock(&sbi->lookup_lock); + + inode = autofs4_get_inode(dir->i_sb, ino); ++ if (!inode) { ++ if (!dentry->d_fsdata) ++ kfree(ino); ++ return -ENOMEM; ++ } + d_add(dentry, inode); + + if (dir == dir->i_sb->s_root->d_inode) +@@ -922,44 +836,6 @@ static inline int autofs4_get_protosubve + } + + /* +- * Tells the daemon whether we need to reghost or not. Also, clears +- * the reghost_needed flag. +- */ +-static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- +- DPRINTK("returning %d", sbi->needs_reghost); +- +- status = put_user(sbi->needs_reghost, p); +- if (status) +- return status; +- +- sbi->needs_reghost = 0; +- return 0; +-} +- +-/* +- * Enable / Disable reghosting ioctl() operation +- */ +-static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) +-{ +- int status; +- int val; +- +- status = get_user(val, p); +- +- DPRINTK("reghost = %d", val); +- +- if (status) +- return status; +- +- /* turn on/off reghosting, with the val */ +- sbi->reghost_enabled = val; +- return 0; +-} +- +-/* + * Tells the daemon whether it can umount the autofs mount. + */ + static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) +@@ -1023,11 +899,6 @@ static int autofs4_root_ioctl(struct ino + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi, p); + +- case AUTOFS_IOC_TOGGLEREGHOST: +- return autofs4_toggle_reghost(sbi, p); +- case AUTOFS_IOC_ASKREGHOST: +- return autofs4_ask_reghost(sbi, p); +- + case AUTOFS_IOC_ASKUMOUNT: + return autofs4_ask_umount(filp->f_path.mnt, p); + +--- linux-2.6.26.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.26/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __func__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -52,12 +72,18 @@ struct autofs_info { + + int flags; + +- struct list_head rehash; ++ struct completion expire_complete; ++ ++ struct list_head active; ++ struct list_head expiring; + + struct autofs_sb_info *sbi; + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -68,15 +94,14 @@ struct autofs_info { + }; + + #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ + + struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ +- unsigned int hash; +- unsigned int len; +- char *name; ++ struct qstr name; + u32 dev; + u64 ino; + uid_t uid; +@@ -85,15 +110,11 @@ struct autofs_wait_queue { + pid_t tgid; + /* This is for status reporting upon return */ + int status; +- atomic_t wait_ctr; ++ unsigned int wait_ctr; + }; + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -112,8 +133,9 @@ struct autofs_sb_info { + struct mutex wq_mutex; + spinlock_t fs_lock; + struct autofs_wait_queue *queues; /* Wait queue pointer */ +- spinlock_t rehash_lock; +- struct list_head rehash_list; ++ spinlock_t lookup_lock; ++ struct list_head active_list; ++ struct list_head expiring_list; + }; + + static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +@@ -138,18 +160,14 @@ static inline int autofs4_oz_mode(struct + static inline int autofs4_ispending(struct dentry *dentry) + { + struct autofs_info *inf = autofs4_dentry_ino(dentry); +- int pending = 0; + + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + return 1; + +- if (inf) { +- spin_lock(&inf->sbi->fs_lock); +- pending = inf->flags & AUTOFS_INF_EXPIRING; +- spin_unlock(&inf->sbi->fs_lock); +- } ++ if (inf->flags & AUTOFS_INF_EXPIRING) ++ return 1; + +- return pending; ++ return 0; + } + + static inline void autofs4_copy_atime(struct file *src, struct file *dst) +@@ -164,11 +182,25 @@ void autofs4_free_ino(struct autofs_info + + /* Expiration */ + int is_autofs4_dentry(struct dentry *); ++int autofs4_expire_wait(struct dentry *dentry); + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.26.orig/fs/autofs4/inode.c ++++ linux-2.6.26/fs/autofs4/inode.c +@@ -24,8 +24,10 @@ + + static void ino_lnkfree(struct autofs_info *ino) + { +- kfree(ino->u.symlink); +- ino->u.symlink = NULL; ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } + } + + struct autofs_info *autofs4_init_ino(struct autofs_info *ino, +@@ -41,16 +43,20 @@ struct autofs_info *autofs4_init_ino(str + if (ino == NULL) + return NULL; + +- ino->flags = 0; +- ino->mode = mode; +- ino->inode = NULL; +- ino->dentry = NULL; +- ino->size = 0; +- +- INIT_LIST_HEAD(&ino->rehash); ++ if (!reinit) { ++ ino->flags = 0; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ INIT_LIST_HEAD(&ino->active); ++ INIT_LIST_HEAD(&ino->expiring); ++ atomic_set(&ino->count, 0); ++ } + ++ ino->uid = 0; ++ ino->gid = 0; ++ ino->mode = mode; + ino->last_used = jiffies; +- atomic_set(&ino->count, 0); + + ino->sbi = sbi; + +@@ -159,8 +165,8 @@ void autofs4_kill_sb(struct super_block + if (!sbi) + goto out_kill_sb; + +- if (!sbi->catatonic) +- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ ++ /* Free wait queues, close pipe */ ++ autofs4_catatonic_mode(sbi); + + /* Clean up and release dangling references */ + autofs4_force_release(sbi); +@@ -191,9 +197,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -278,13 +284,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -332,14 +338,15 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); + spin_lock_init(&sbi->fs_lock); + sbi->queues = NULL; +- spin_lock_init(&sbi->rehash_lock); +- INIT_LIST_HEAD(&sbi->rehash_list); ++ spin_lock_init(&sbi->lookup_lock); ++ INIT_LIST_HEAD(&sbi->active_list); ++ INIT_LIST_HEAD(&sbi->expiring_list); + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -373,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.26.orig/fs/autofs4/waitq.c ++++ linux-2.6.26/fs/autofs4/waitq.c +@@ -28,6 +28,12 @@ void autofs4_catatonic_mode(struct autof + { + struct autofs_wait_queue *wq, *nwq; + ++ mutex_lock(&sbi->wq_mutex); ++ if (sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return; ++ } ++ + DPRINTK("entering catatonic mode"); + + sbi->catatonic = 1; +@@ -36,13 +42,18 @@ void autofs4_catatonic_mode(struct autof + while (wq) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ +- kfree(wq->name); +- wq->name = NULL; ++ if (wq->name.name) { ++ kfree(wq->name.name); ++ wq->name.name = NULL; ++ } ++ wq->wait_ctr--; + wake_up_interruptible(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; ++ sbi->pipefd = -1; ++ mutex_unlock(&sbi->wq_mutex); + } + + static int autofs4_write(struct file *file, const void *addr, int bytes) +@@ -89,10 +100,11 @@ static void autofs4_notify_daemon(struct + union autofs_packet_union v4_pkt; + union autofs_v5_packet_union v5_pkt; + } pkt; ++ struct file *pipe = NULL; + size_t pktsz; + + DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", +- wq->wait_queue_token, wq->len, wq->name, type); ++ wq->wait_queue_token, wq->name.len, wq->name.name, type); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + +@@ -107,9 +119,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; +- mp->len = wq->len; +- memcpy(mp->name, wq->name, wq->len); +- mp->name[wq->len] = '\0'; ++ mp->len = wq->name.len; ++ memcpy(mp->name, wq->name.name, wq->name.len); ++ mp->name[wq->name.len] = '\0'; + break; + } + case autofs_ptype_expire_multi: +@@ -119,9 +131,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; +- ep->len = wq->len; +- memcpy(ep->name, wq->name, wq->len); +- ep->name[wq->len] = '\0'; ++ ep->len = wq->name.len; ++ memcpy(ep->name, wq->name.name, wq->name.len); ++ ep->name[wq->name.len] = '\0'; + break; + } + /* +@@ -138,9 +150,9 @@ static void autofs4_notify_daemon(struct + pktsz = sizeof(*packet); + + packet->wait_queue_token = wq->wait_queue_token; +- packet->len = wq->len; +- memcpy(packet->name, wq->name, wq->len); +- packet->name[wq->len] = '\0'; ++ packet->len = wq->name.len; ++ memcpy(packet->name, wq->name.name, wq->name.len); ++ packet->name[wq->name.len] = '\0'; + packet->dev = wq->dev; + packet->ino = wq->ino; + packet->uid = wq->uid; +@@ -154,8 +166,19 @@ static void autofs4_notify_daemon(struct + return; + } + +- if (autofs4_write(sbi->pipe, &pkt, pktsz)) +- autofs4_catatonic_mode(sbi); ++ /* Check if we have become catatonic */ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ pipe = sbi->pipe; ++ get_file(pipe); ++ } ++ mutex_unlock(&sbi->wq_mutex); ++ ++ if (pipe) { ++ if (autofs4_write(pipe, &pkt, pktsz)) ++ autofs4_catatonic_mode(sbi); ++ fput(pipe); ++ } + } + + static int autofs4_getpath(struct autofs_sb_info *sbi, +@@ -191,58 +214,55 @@ static int autofs4_getpath(struct autofs + } + + static struct autofs_wait_queue * +-autofs4_find_wait(struct autofs_sb_info *sbi, +- char *name, unsigned int hash, unsigned int len) ++autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) + { + struct autofs_wait_queue *wq; + + for (wq = sbi->queues; wq; wq = wq->next) { +- if (wq->hash == hash && +- wq->len == len && +- wq->name && !memcmp(wq->name, name, len)) ++ if (wq->name.hash == qstr->hash && ++ wq->name.len == qstr->len && ++ wq->name.name && ++ !memcmp(wq->name.name, qstr->name, qstr->len)) + break; + } + return wq; + } + +-int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, +- enum autofs_notify notify) ++/* ++ * Check if we have a valid request. ++ * Returns ++ * 1 if the request should continue. ++ * In this case we can return an autofs_wait_queue entry if one is ++ * found or NULL to idicate a new wait needs to be created. ++ * 0 or a negative errno if the request shouldn't continue. ++ */ ++static int validate_request(struct autofs_wait_queue **wait, ++ struct autofs_sb_info *sbi, ++ struct qstr *qstr, ++ struct dentry*dentry, enum autofs_notify notify) + { +- struct autofs_info *ino; + struct autofs_wait_queue *wq; +- char *name; +- unsigned int len = 0; +- unsigned int hash = 0; +- int status, type; +- +- /* In catatonic mode, we don't wait for nobody */ +- if (sbi->catatonic) +- return -ENOENT; +- +- name = kmalloc(NAME_MAX + 1, GFP_KERNEL); +- if (!name) +- return -ENOMEM; ++ struct autofs_info *ino; + +- /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) +- len = sprintf(name, "%p", dentry); +- else { +- len = autofs4_getpath(sbi, dentry, &name); +- if (!len) { +- kfree(name); +- return -ENOENT; +- } ++ /* Wait in progress, continue; */ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- hash = full_name_hash(name, len); + +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); +- return -EINTR; +- } ++ *wait = NULL; + +- wq = autofs4_find_wait(sbi, name, hash, len); ++ /* If we don't yet have any info this is a new request */ + ino = autofs4_dentry_ino(dentry); +- if (!wq && ino && notify == NFY_NONE) { ++ if (!ino) ++ return 1; ++ ++ /* ++ * If we've been asked to wait on an existing expire (NFY_NONE) ++ * but there is no wait in the queue ... ++ */ ++ if (notify == NFY_NONE) { + /* + * Either we've betean the pending expire to post it's + * wait or it finished while we waited on the mutex. +@@ -253,13 +273,14 @@ int autofs4_wait(struct autofs_sb_info * + while (ino->flags & AUTOFS_INF_EXPIRING) { + mutex_unlock(&sbi->wq_mutex); + schedule_timeout_interruptible(HZ/10); +- if (mutex_lock_interruptible(&sbi->wq_mutex)) { +- kfree(name); ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) + return -EINTR; ++ ++ wq = autofs4_find_wait(sbi, qstr); ++ if (wq) { ++ *wait = wq; ++ return 1; + } +- wq = autofs4_find_wait(sbi, name, hash, len); +- if (wq) +- break; + } + + /* +@@ -267,18 +288,90 @@ int autofs4_wait(struct autofs_sb_info * + * cases where we wait on NFY_NONE neither depend on the + * return status of the wait. + */ +- if (!wq) { +- kfree(name); +- mutex_unlock(&sbi->wq_mutex); ++ return 0; ++ } ++ ++ /* ++ * If we've been asked to trigger a mount and the request ++ * completed while we waited on the mutex ... ++ */ ++ if (notify == NFY_MOUNT) { ++ /* ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) + return 0; ++ } ++ ++ return 1; ++} ++ ++int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ++ enum autofs_notify notify) ++{ ++ struct autofs_wait_queue *wq; ++ struct qstr qstr; ++ char *name; ++ int status, ret, type; ++ ++ /* In catatonic mode, we don't wait for nobody */ ++ if (sbi->catatonic) ++ return -ENOENT; ++ ++ if (!dentry->d_inode) { ++ /* ++ * A wait for a negative dentry is invalid for certain ++ * cases. A direct or offset mount "always" has its mount ++ * point directory created and so the request dentry must ++ * be positive or the map key doesn't exist. The situation ++ * is very similar for indirect mounts except only dentrys ++ * in the root of the autofs file system may be negative. ++ */ ++ if (autofs_type_trigger(sbi->type)) ++ return -ENOENT; ++ else if (!IS_ROOT(dentry->d_parent)) ++ return -ENOENT; ++ } ++ ++ name = kmalloc(NAME_MAX + 1, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ /* If this is a direct mount request create a dummy name */ ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) ++ qstr.len = sprintf(name, "%p", dentry); ++ else { ++ qstr.len = autofs4_getpath(sbi, dentry, &name); ++ if (!qstr.len) { ++ kfree(name); ++ return -ENOENT; + } + } ++ qstr.name = name; ++ qstr.hash = full_name_hash(name, qstr.len); ++ ++ if (mutex_lock_interruptible(&sbi->wq_mutex)) { ++ kfree(qstr.name); ++ return -EINTR; ++ } ++ ++ ret = validate_request(&wq, sbi, &qstr, dentry, notify); ++ if (ret <= 0) { ++ if (ret == 0) ++ mutex_unlock(&sbi->wq_mutex); ++ kfree(qstr.name); ++ return ret; ++ } + + if (!wq) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if (!wq) { +- kfree(name); ++ kfree(qstr.name); + mutex_unlock(&sbi->wq_mutex); + return -ENOMEM; + } +@@ -289,9 +382,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->next = sbi->queues; + sbi->queues = wq; + init_waitqueue_head(&wq->queue); +- wq->hash = hash; +- wq->name = name; +- wq->len = len; ++ memcpy(&wq->name, &qstr, sizeof(struct qstr)); + wq->dev = autofs4_get_dev(sbi); + wq->ino = autofs4_get_ino(sbi); + wq->uid = current->uid; +@@ -299,7 +390,7 @@ int autofs4_wait(struct autofs_sb_info * + wq->pid = current->pid; + wq->tgid = current->tgid; + wq->status = -EINTR; /* Status return if interrupted */ +- atomic_set(&wq->wait_ctr, 2); ++ wq->wait_ctr = 2; + mutex_unlock(&sbi->wq_mutex); + + if (sbi->version < 5) { +@@ -309,38 +400,35 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } + + DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + + /* autofs4_notify_daemon() may block */ + autofs4_notify_daemon(sbi, wq, type); + } else { +- atomic_inc(&wq->wait_ctr); ++ wq->wait_ctr++; + mutex_unlock(&sbi->wq_mutex); +- kfree(name); ++ kfree(qstr.name); + DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", +- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); ++ (unsigned long) wq->wait_queue_token, wq->name.len, ++ wq->name.name, notify); + } + +- /* wq->name is NULL if and only if the lock is already released */ +- +- if (sbi->catatonic) { +- /* We might have slept, so check again for catatonic mode */ +- wq->status = -ENOENT; +- kfree(wq->name); +- wq->name = NULL; +- } +- +- if (wq->name) { ++ /* ++ * wq->name.name is NULL iff the lock is already released ++ * or the mount has been made catatonic. ++ */ ++ if (wq->name.name) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; +@@ -351,7 +439,7 @@ int autofs4_wait(struct autofs_sb_info * + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + +- wait_event_interruptible(wq->queue, wq->name == NULL); ++ wait_event_interruptible(wq->queue, wq->name.name == NULL); + + spin_lock_irqsave(¤t->sighand->siglock, irqflags); + current->blocked = oldset; +@@ -363,9 +451,45 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ +- if (atomic_dec_and_test(&wq->wait_ctr)) ++ mutex_lock(&sbi->wq_mutex); ++ if (!--wq->wait_ctr) + kfree(wq); ++ mutex_unlock(&sbi->wq_mutex); + + return status; + } +@@ -387,16 +511,13 @@ int autofs4_wait_release(struct autofs_s + } + + *wql = wq->next; /* Unlink from chain */ +- mutex_unlock(&sbi->wq_mutex); +- kfree(wq->name); +- wq->name = NULL; /* Do not wait on this queue */ +- ++ kfree(wq->name.name); ++ wq->name.name = NULL; /* Do not wait on this queue */ + wq->status = status; +- +- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ ++ wake_up_interruptible(&wq->queue); ++ if (!--wq->wait_ctr) + kfree(wq); +- else +- wake_up_interruptible(&wq->queue); ++ mutex_unlock(&sbi->wq_mutex); + + return 0; + } +--- linux-2.6.26.orig/fs/autofs4/expire.c ++++ linux-2.6.26/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -259,13 +272,15 @@ static struct dentry *autofs4_expire_dir + now = jiffies; + timeout = sbi->exp_timeout; + +- /* Lock the tree as we must expire as a whole */ + spin_lock(&sbi->fs_lock); + if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { + struct autofs_info *ino = autofs4_dentry_ino(root); +- +- /* Set this flag early to catch sys_chdir and the like */ ++ if (d_mountpoint(root)) { ++ ino->flags |= AUTOFS_INF_MOUNTPOINT; ++ root->d_mounted--; ++ } + ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); + spin_unlock(&sbi->fs_lock); + return root; + } +@@ -281,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -292,6 +307,8 @@ static struct dentry *autofs4_expire_ind + struct list_head *next; + int do_now = how & AUTOFS_EXP_IMMEDIATE; + int exp_leaves = how & AUTOFS_EXP_LEAVES; ++ struct autofs_info *ino; ++ unsigned int ino_count; + + if (!root) + return NULL; +@@ -316,6 +333,9 @@ static struct dentry *autofs4_expire_ind + dentry = dget(dentry); + spin_unlock(&dcache_lock); + ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ + /* + * Case 1: (i) indirect mount or top level pseudo direct mount + * (autofs-4.1). +@@ -326,6 +346,11 @@ static struct dentry *autofs4_expire_ind + 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 (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + /* Can we umount this guy */ + if (autofs4_mount_busy(mnt, dentry)) + goto next; +@@ -343,23 +368,25 @@ static struct dentry *autofs4_expire_ind + + /* Case 2: tree mount, expire iff entire tree is not busy */ + if (!exp_leaves) { +- /* Lock the tree as we must expire as a whole */ +- spin_lock(&sbi->fs_lock); +- if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { +- struct autofs_info *inf = autofs4_dentry_ino(dentry); ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; + +- /* Set this flag early to catch sys_chdir and the like */ +- inf->flags |= AUTOFS_INF_EXPIRING; +- spin_unlock(&sbi->fs_lock); ++ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { + expired = dentry; + goto found; + } +- spin_unlock(&sbi->fs_lock); + /* + * Case 3: pseudo direct mount, expire individual leaves + * (autofs-4.1). + */ + } else { ++ /* Path walk currently on this dentry? */ ++ ino_count = atomic_read(&ino->count) + 1; ++ if (atomic_read(&dentry->d_count) > ino_count) ++ goto next; ++ + expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); + if (expired) { + dput(dentry); +@@ -367,6 +394,7 @@ static struct dentry *autofs4_expire_ind + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; +@@ -377,12 +405,45 @@ next: + found: + DPRINTK("returning %p %.*s", + expired, (int)expired->d_name.len, expired->d_name.name); ++ ino = autofs4_dentry_ino(expired); ++ ino->flags |= AUTOFS_INF_EXPIRING; ++ init_completion(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + spin_lock(&dcache_lock); + list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); + spin_unlock(&dcache_lock); + return expired; + } + ++int autofs4_expire_wait(struct dentry *dentry) ++{ ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ struct autofs_info *ino = autofs4_dentry_ino(dentry); ++ int status; ++ ++ /* Block on any pending expire */ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_EXPIRING) { ++ spin_unlock(&sbi->fs_lock); ++ ++ DPRINTK("waiting for expire %p name=%.*s", ++ dentry, dentry->d_name.len, dentry->d_name.name); ++ ++ status = autofs4_wait(sbi, dentry, NFY_NONE); ++ wait_for_completion(&ino->expire_complete); ++ ++ DPRINTK("expire done status=%d", status); ++ ++ if (d_unhashed(dentry)) ++ return -EAGAIN; ++ ++ return status; ++ } ++ spin_unlock(&sbi->fs_lock); ++ ++ return 0; ++} ++ + /* Perform an expiry operation */ + int autofs4_expire_run(struct super_block *sb, + struct vfsmount *mnt, +@@ -390,7 +451,9 @@ int autofs4_expire_run(struct super_bloc + struct autofs_packet_expire __user *pkt_p) + { + struct autofs_packet_expire pkt; ++ struct autofs_info *ino; + struct dentry *dentry; ++ int ret = 0; + + memset(&pkt,0,sizeof pkt); + +@@ -406,39 +469,59 @@ int autofs4_expire_run(struct super_bloc + dput(dentry); + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) +- return -EFAULT; ++ ret = -EFAULT; + +- return 0; ++ spin_lock(&sbi->fs_lock); ++ ino = autofs4_dentry_ino(dentry); ++ ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); ++ ++ return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; +- +- if (arg && get_user(do_now, arg)) +- return -EFAULT; + +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ +- ino->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); ++ ++ spin_lock(&sbi->fs_lock); ++ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { ++ sb->s_root->d_mounted++; ++ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; ++ } + ino->flags &= ~AUTOFS_INF_EXPIRING; ++ complete_all(&ino->expire_complete); ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + } + + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.26.orig/fs/compat_ioctl.c ++++ linux-2.6.26/fs/compat_ioctl.c +@@ -2350,8 +2350,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) + COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) + COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) +-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) +-COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) + COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) + /* Raw devices */ + COMPATIBLE_IOCTL(RAW_SETBIND) +--- linux-2.6.26.orig/include/linux/auto_fs4.h ++++ linux-2.6.26/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +@@ -98,8 +157,6 @@ union autofs_v5_packet_union { + #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI + #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) +-#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) +-#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) + #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) + + +--- /dev/null ++++ linux-2.6.26/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.26.orig/fs/autofs4/Makefile ++++ linux-2.6.26/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.26/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ inode = nd->path.dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ ino = autofs4_dentry_ino(nd->path.dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_put(&nd.path); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.path.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.path.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.path.dentry->d_inode && ++ nd.path.mnt->mnt_root == nd.path.dentry) { ++ err = 1; ++ magic = nd.path.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.path.dentry); ++ ++ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { ++ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { ++ struct inode *inode = nd.path.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.26.orig/fs/autofs4/init.c ++++ linux-2.6.26/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.26/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.26.orig/include/linux/auto_fs.h ++++ linux-2.6.26/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.27-dev-ioctl-20081029.patch b/patches/autofs4-2.6.27-dev-ioctl-20081029.patch deleted file mode 100644 index 90783dc..0000000 --- a/patches/autofs4-2.6.27-dev-ioctl-20081029.patch +++ /dev/null @@ -1,1919 +0,0 @@ -Index: linux-2.6.27/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.27/fs/autofs4/autofs_i.h -@@ -14,6 +14,7 @@ - /* Internal header file for autofs */ - - #include -+#include - #include - #include - -@@ -21,7 +22,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - --#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) -+#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) - - #include - #include -@@ -37,11 +39,27 @@ - /* #define DEBUG */ - - #ifdef DEBUG --#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __func__ , ##args); } while(0) -+#define DPRINTK(fmt, args...) \ -+do { \ -+ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) - #else --#define DPRINTK(fmt,args...) do {} while(0) -+#define DPRINTK(fmt, args...) do {} while (0) - #endif - -+#define AUTOFS_WARN(fmt, args...) \ -+do { \ -+ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) -+ -+#define AUTOFS_ERROR(fmt, args...) \ -+do { \ -+ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ -+ current->pid, __func__, ##args); \ -+} while (0) -+ - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -63,6 +81,9 @@ struct autofs_info { - unsigned long last_used; - atomic_t count; - -+ uid_t uid; -+ gid_t gid; -+ - mode_t mode; - size_t size; - -@@ -165,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when); - int autofs4_expire_multi(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, int __user *); -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int how); -+ -+/* Device node initialization */ -+ -+int autofs_dev_ioctl_init(void); -+void autofs_dev_ioctl_exit(void); - - /* Operations structures */ - -Index: linux-2.6.27/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/inode.c -+++ linux-2.6.27/fs/autofs4/inode.c -@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str - atomic_set(&ino->count, 0); - } - -+ ino->uid = 0; -+ ino->gid = 0; - ino->mode = mode; - ino->last_used = jiffies; - -@@ -195,9 +197,9 @@ static int autofs4_show_options(struct s - seq_printf(m, ",minproto=%d", sbi->min_proto); - seq_printf(m, ",maxproto=%d", sbi->max_proto); - -- if (sbi->type & AUTOFS_TYPE_OFFSET) -+ if (autofs_type_offset(sbi->type)) - seq_printf(m, ",offset"); -- else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ else if (autofs_type_direct(sbi->type)) - seq_printf(m, ",direct"); - else - seq_printf(m, ",indirect"); -@@ -282,13 +284,13 @@ static int parse_options(char *options, - *maxproto = option; - break; - case Opt_indirect: -- *type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(type); - break; - case Opt_direct: -- *type = AUTOFS_TYPE_DIRECT; -+ set_autofs_type_direct(type); - break; - case Opt_offset: -- *type = AUTOFS_TYPE_OFFSET; -+ set_autofs_type_offset(type); - break; - default: - return 1; -@@ -336,7 +338,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = AUTOFS_TYPE_INDIRECT; -+ set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -378,7 +380,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ root_inode->i_op = autofs_type_trigger(sbi->type) ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.27/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/waitq.c -+++ linux-2.6.27/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (autofs_type_trigger(sbi->type)) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ type = autofs_type_trigger(sbi->type) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -@@ -457,6 +457,40 @@ int autofs4_wait(struct autofs_sb_info * - - status = wq->status; - -+ /* -+ * For direct and offset mounts we need to track the requester's -+ * uid and gid in the dentry info struct. This is so it can be -+ * supplied, on request, by the misc device ioctl interface. -+ * This is needed during daemon resatart when reconnecting -+ * to existing, active, autofs mounts. The uid and gid (and -+ * related string values) may be used for macro substitution -+ * in autofs mount maps. -+ */ -+ if (!status) { -+ struct autofs_info *ino; -+ struct dentry *de = NULL; -+ -+ /* direct mount or browsable map */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) { -+ /* If not lookup actual dentry used */ -+ de = d_lookup(dentry->d_parent, &dentry->d_name); -+ if (de) -+ ino = autofs4_dentry_ino(de); -+ } -+ -+ /* Set mount requester */ -+ if (ino) { -+ spin_lock(&sbi->fs_lock); -+ ino->uid = wq->uid; -+ ino->gid = wq->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+ if (de) -+ dput(de); -+ } -+ - /* Are we the last process to need status? */ - mutex_lock(&sbi->wq_mutex); - if (!--wq->wait_ctr) -Index: linux-2.6.27/Documentation/filesystems/autofs4-mount-control.txt -=================================================================== ---- /dev/null -+++ linux-2.6.27/Documentation/filesystems/autofs4-mount-control.txt -@@ -0,0 +1,414 @@ -+ -+Miscellaneous Device control operations for the autofs4 kernel module -+==================================================================== -+ -+The problem -+=========== -+ -+There is a problem with active restarts in autofs (that is to say -+restarting autofs when there are busy mounts). -+ -+During normal operation autofs uses a file descriptor opened on the -+directory that is being managed in order to be able to issue control -+operations. Using a file descriptor gives ioctl operations access to -+autofs specific information stored in the super block. The operations -+are things such as setting an autofs mount catatonic, setting the -+expire timeout and requesting expire checks. As is explained below, -+certain types of autofs triggered mounts can end up covering an autofs -+mount itself which prevents us being able to use open(2) to obtain a -+file descriptor for these operations if we don't already have one open. -+ -+Currently autofs uses "umount -l" (lazy umount) to clear active mounts -+at restart. While using lazy umount works for most cases, anything that -+needs to walk back up the mount tree to construct a path, such as -+getcwd(2) and the proc file system /proc//cwd, no longer works -+because the point from which the path is constructed has been detached -+from the mount tree. -+ -+The actual problem with autofs is that it can't reconnect to existing -+mounts. Immediately one thinks of just adding the ability to remount -+autofs file systems would solve it, but alas, that can't work. This is -+because autofs direct mounts and the implementation of "on demand mount -+and expire" of nested mount trees have the file system mounted directly -+on top of the mount trigger directory dentry. -+ -+For example, there are two types of automount maps, direct (in the kernel -+module source you will see a third type called an offset, which is just -+a direct mount in disguise) and indirect. -+ -+Here is a master map with direct and indirect map entries: -+ -+/- /etc/auto.direct -+/test /etc/auto.indirect -+ -+and the corresponding map files: -+ -+/etc/auto.direct: -+ -+/automount/dparse/g6 budgie:/autofs/export1 -+/automount/dparse/g1 shark:/autofs/export1 -+and so on. -+ -+/etc/auto.indirect: -+ -+g1 shark:/autofs/export1 -+g6 budgie:/autofs/export1 -+and so on. -+ -+For the above indirect map an autofs file system is mounted on /test and -+mounts are triggered for each sub-directory key by the inode lookup -+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for -+example. -+ -+The way that direct mounts are handled is by making an autofs mount on -+each full path, such as /automount/dparse/g1, and using it as a mount -+trigger. So when we walk on the path we mount shark:/autofs/export1 "on -+top of this mount point". Since these are always directories we can -+use the follow_link inode operation to trigger the mount. -+ -+But, each entry in direct and indirect maps can have offsets (making -+them multi-mount map entries). -+ -+For example, an indirect mount map entry could also be: -+ -+g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export1 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+and a similarly a direct mount map entry could also be: -+ -+/automount/dparse/g1 \ -+ / shark:/autofs/export5/testing/test \ -+ /s1 shark:/autofs/export/testing/test/s1 \ -+ /s2 shark:/autofs/export5/testing/test/s2 \ -+ /s1/ss1 shark:/autofs/export2 \ -+ /s2/ss2 shark:/autofs/export2 -+ -+One of the issues with version 4 of autofs was that, when mounting an -+entry with a large number of offsets, possibly with nesting, we needed -+to mount and umount all of the offsets as a single unit. Not really a -+problem, except for people with a large number of offsets in map entries. -+This mechanism is used for the well known "hosts" map and we have seen -+cases (in 2.4) where the available number of mounts are exhausted or -+where the number of privileged ports available is exhausted. -+ -+In version 5 we mount only as we go down the tree of offsets and -+similarly for expiring them which resolves the above problem. There is -+somewhat more detail to the implementation but it isn't needed for the -+sake of the problem explanation. The one important detail is that these -+offsets are implemented using the same mechanism as the direct mounts -+above and so the mount points can be covered by a mount. -+ -+The current autofs implementation uses an ioctl file descriptor opened -+on the mount point for control operations. The references held by the -+descriptor are accounted for in checks made to determine if a mount is -+in use and is also used to access autofs file system information held -+in the mount super block. So the use of a file handle needs to be -+retained. -+ -+ -+The Solution -+============ -+ -+To be able to restart autofs leaving existing direct, indirect and -+offset mounts in place we need to be able to obtain a file handle -+for these potentially covered autofs mount points. Rather than just -+implement an isolated operation it was decided to re-implement the -+existing ioctl interface and add new operations to provide this -+functionality. -+ -+In addition, to be able to reconstruct a mount tree that has busy mounts, -+the uid and gid of the last user that triggered the mount needs to be -+available because these can be used as macro substitution variables in -+autofs maps. They are recorded at mount request time and an operation -+has been added to retrieve them. -+ -+Since we're re-implementing the control interface, a couple of other -+problems with the existing interface have been addressed. First, when -+a mount or expire operation completes a status is returned to the -+kernel by either a "send ready" or a "send fail" operation. The -+"send fail" operation of the ioctl interface could only ever send -+ENOENT so the re-implementation allows user space to send an actual -+status. Another expensive operation in user space, for those using -+very large maps, is discovering if a mount is present. Usually this -+involves scanning /proc/mounts and since it needs to be done quite -+often it can introduce significant overhead when there are many entries -+in the mount table. An operation to lookup the mount status of a mount -+point dentry (covered or not) has also been added. -+ -+Current kernel development policy recommends avoiding the use of the -+ioctl mechanism in favor of systems such as Netlink. An implementation -+using this system was attempted to evaluate its suitability and it was -+found to be inadequate, in this case. The Generic Netlink system was -+used for this as raw Netlink would lead to a significant increase in -+complexity. There's no question that the Generic Netlink system is an -+elegant solution for common case ioctl functions but it's not a complete -+replacement probably because it's primary purpose in life is to be a -+message bus implementation rather than specifically an ioctl replacement. -+While it would be possible to work around this there is one concern -+that lead to the decision to not use it. This is that the autofs -+expire in the daemon has become far to complex because umount -+candidates are enumerated, almost for no other reason than to "count" -+the number of times to call the expire ioctl. This involves scanning -+the mount table which has proved to be a big overhead for users with -+large maps. The best way to improve this is try and get back to the -+way the expire was done long ago. That is, when an expire request is -+issued for a mount (file handle) we should continually call back to -+the daemon until we can't umount any more mounts, then return the -+appropriate status to the daemon. At the moment we just expire one -+mount at a time. A Generic Netlink implementation would exclude this -+possibility for future development due to the requirements of the -+message bus architecture. -+ -+ -+autofs4 Miscellaneous Device mount control interface -+==================================================== -+ -+The control interface is opening a device node, typically /dev/autofs. -+ -+All the ioctls use a common structure to pass the needed parameter -+information and return operation results: -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+The ioctlfd field is a mount point file descriptor of an autofs mount -+point. It is returned by the open call and is used by all calls except -+the check for whether a given path is a mount point, where it may -+optionally be used to check a specific mount corresponding to a given -+mount point file descriptor, and when requesting the uid and gid of the -+last successful mount on a directory within the autofs file system. -+ -+The anonymous union is used to communicate parameters and results of calls -+made as described below. -+ -+The path field is used to pass a path where it is needed and the size field -+is used account for the increased structure length when translating the -+structure sent from user space. -+ -+This structure can be initialized before setting specific fields by using -+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). -+ -+All of the ioctls perform a copy of this structure from user space to -+kernel space and return -EINVAL if the size parameter is smaller than -+the structure size itself, -ENOMEM if the kernel memory allocation fails -+or -EFAULT if the copy itself fails. Other checks include a version check -+of the compiled in user space version against the module version and a -+mismatch results in a -EINVAL return. If the size field is greater than -+the structure size then a path is assumed to be present and is checked to -+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is -+returned. Following these checks, for all ioctl commands except -+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and -+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is -+not a valid descriptor or doesn't correspond to an autofs mount point -+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is -+returned. -+ -+ -+The ioctls -+========== -+ -+An example of an implementation which uses this interface can be seen -+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the -+distribution tar available for download from kernel.org in directory -+/pub/linux/daemons/autofs/v5. -+ -+The device node ioctl operations implemented by this interface are: -+ -+ -+AUTOFS_DEV_IOCTL_VERSION -+------------------------ -+ -+Get the major and minor version of the autofs4 device ioctl kernel module -+implementation. It requires an initialized struct autofs_dev_ioctl as an -+input parameter and sets the version information in the passed in structure. -+It returns 0 on success or the error -EINVAL if a version mismatch is -+detected. -+ -+ -+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD -+------------------------------------------------------------------ -+ -+Get the major and minor version of the autofs4 protocol version understood -+by loaded module. This call requires an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to a valid autofs mount point descriptor -+and sets the requested version number in structure field protover.version -+and ptotosubver.sub_version respectively. These commands return 0 on -+success or one of the negative error codes if validation fails. -+ -+ -+AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD -+------------------------------------------------------------------ -+ -+Obtain and release a file descriptor for an autofs managed mount point -+path. The open call requires an initialized struct autofs_dev_ioctl with -+the the path field set and the size field adjusted appropriately as well -+as the openmount.devid field set to the device number of the autofs mount. -+The device number of an autofs mounted filesystem can be obtained by using -+the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path -+and autofs mount type, as described below. The close call requires an -+initialized struct autofs_dev_ioct with the ioctlfd field set to the -+descriptor obtained from the open call. The release of the file descriptor -+can also be done with close(2) so any open descriptors will also be -+closed at process exit. The close call is included in the implemented -+operations largely for completeness and to provide for a consistent -+user space implementation. -+ -+ -+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD -+-------------------------------------------------------- -+ -+Return mount and expire result status from user space to the kernel. -+Both of these calls require an initialized struct autofs_dev_ioctl -+with the ioctlfd field set to the descriptor obtained from the open -+call and the ready.token or fail.token field set to the wait queue -+token number, received by user space in the foregoing mount or expire -+request. The fail.status field is set to the status to be returned when -+sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. -+ -+ -+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD -+------------------------------ -+ -+Set the pipe file descriptor used for kernel communication to the daemon. -+Normally this is set at mount time using an option but when reconnecting -+to a existing mount we need to use this to tell the autofs mount about -+the new kernel pipe descriptor. In order to protect mounts against -+incorrectly setting the pipe descriptor we also require that the autofs -+mount be catatonic (see next call). -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+the setpipefd.pipefd field set to descriptor of the pipe. On success -+the call also sets the process group id used to identify the controlling -+process (eg. the owning automount(8) daemon) to the process group of -+the caller. -+ -+ -+AUTOFS_DEV_IOCTL_CATATONIC_CMD -+------------------------------ -+ -+Make the autofs mount point catatonic. The autofs mount will no longer -+issue mount requests, the kernel communication pipe descriptor is released -+and any remaining waits in the queue released. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+ -+ -+AUTOFS_DEV_IOCTL_TIMEOUT_CMD -+---------------------------- -+ -+Set the expire timeout for mounts withing an autofs mount point. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. -+The timeout.timeout field is set to the desired timeout and this -+field is set to the value of the value of the current timeout of -+the mount upon successful completion. -+ -+ -+AUTOFS_DEV_IOCTL_REQUESTER_CMD -+------------------------------ -+ -+Return the uid and gid of the last process to successfully trigger a the -+mount on the given path dentry. -+ -+The call requires an initialized struct autofs_dev_ioctl with the path -+field set to the mount point in question and the size field adjusted -+appropriately as well as the ioctlfd field set to the descriptor obtained -+from the open call. Upon return the struct fields requester.uid and -+requester.gid contain the uid and gid respectively. -+ -+When reconstructing an autofs mount tree with active mounts we need to -+re-connect to mounts that may have used the original process uid and -+gid (or string variations of them) for mount lookups within the map entry. -+This call provides the ability to obtain this uid and gid so they may be -+used by user space for the mount map lookups. -+ -+ -+AUTOFS_DEV_IOCTL_EXPIRE_CMD -+--------------------------- -+ -+Issue an expire request to the kernel for an autofs mount. Typically -+this ioctl is called until no further expire candidates are found. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call. In -+addition an immediate expire, independent of the mount timeout, can be -+requested by setting the expire.how field to 1. If no expire candidates -+can be found the ioctl returns -1 with errno set to EAGAIN. -+ -+This call causes the kernel module to check the mount corresponding -+to the given ioctlfd for mounts that can be expired, issues an expire -+request back to the daemon and waits for completion. -+ -+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD -+------------------------------ -+ -+Checks if an autofs mount point is in use. -+ -+The call requires an initialized struct autofs_dev_ioctl with the -+ioctlfd field set to the descriptor obtained from the open call and -+it returns the result in the askumount.may_umount field, 1 for busy -+and 0 otherwise. -+ -+ -+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD -+--------------------------------- -+ -+Check if the given path is a mountpoint. -+ -+The call requires an initialized struct autofs_dev_ioctl. There are two -+possible variations. Both use the path field set to the path of the mount -+point to check and the size field must be adjusted appropriately. One uses -+the ioctlfd field to identify a specific mount point to check while the -+other variation uses the path and optionaly the ismountpoint.in.type -+field set to an autofs mount type. The call returns 1 if this is a mount -+point and sets the ismountpoint.out.devid field to the device number of -+the mount and the ismountpoint.out.magic field to the relevant super -+block magic number (described below) or 0 if it isn't a mountpoint. In -+both cases the the device number (as returned by new_encode_dev()) is -+returned in the ismountpoint.out.devid field. -+ -+If supplied with a file descriptor we're looking for a specific mount, -+not necessarily at the top of the mounted stack. In this case the path -+the descriptor corresponds to is considered a mountpoint if it is itself -+a mountpoint or contains a mount, such as a multi-mount without a root -+mount. In this case we return 1 if the descriptor corresponds to a mount -+point and and also returns the super magic of the covering mount if there -+is one or 0 if it isn't a mountpoint. -+ -+If a path is supplied (and the ioctlfd field is set to -1) then the path -+is looked up and is checked to see if it is the root of a mount. If a -+type is also given we are looking for a particular autofs mount and if -+a match isn't found a fail is returned. If the the located path is the -+root of a mount 1 is returned along with the super magic of the mount -+or 0 otherwise. -+ -Index: linux-2.6.27/fs/autofs4/Makefile -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/Makefile -+++ linux-2.6.27/fs/autofs4/Makefile -@@ -4,4 +4,4 @@ - - obj-$(CONFIG_AUTOFS4_FS) += autofs4.o - --autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o -+autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o -Index: linux-2.6.27/fs/autofs4/dev-ioctl.c -=================================================================== ---- /dev/null -+++ linux-2.6.27/fs/autofs4/dev-ioctl.c -@@ -0,0 +1,841 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "autofs_i.h" -+ -+/* -+ * This module implements an interface for routing autofs ioctl control -+ * commands via a miscellaneous device file. -+ * -+ * The alternate interface is needed because we need to be able open -+ * an ioctl file descriptor on an autofs mount that may be covered by -+ * another mount. This situation arises when starting automount(8) -+ * or other user space daemon which uses direct mounts or offset -+ * mounts (used for autofs lazy mount/umount of nested mount trees), -+ * which have been left busy at at service shutdown. -+ */ -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+typedef int (*ioctl_fn)(struct file *, -+struct autofs_sb_info *, struct autofs_dev_ioctl *); -+ -+static int check_name(const char *name) -+{ -+ if (!strchr(name, '/')) -+ return -EINVAL; -+ return 0; -+} -+ -+/* -+ * Check a string doesn't overrun the chunk of -+ * memory we copied from user land. -+ */ -+static int invalid_str(char *str, void *end) -+{ -+ while ((void *) str <= end) -+ if (!*str++) -+ return 0; -+ return -EINVAL; -+} -+ -+/* -+ * Check that the user compiled against correct version of autofs -+ * misc device code. -+ * -+ * As well as checking the version compatibility this always copies -+ * the kernel interface version out. -+ */ -+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err = 0; -+ -+ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || -+ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { -+ AUTOFS_WARN("ioctl control interface version mismatch: " -+ "kernel(%u.%u), user(%u.%u), cmd(%d)", -+ AUTOFS_DEV_IOCTL_VERSION_MAJOR, -+ AUTOFS_DEV_IOCTL_VERSION_MINOR, -+ param->ver_major, param->ver_minor, cmd); -+ err = -EINVAL; -+ } -+ -+ /* Fill in the kernel version. */ -+ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ -+ return err; -+} -+ -+/* -+ * Copy parameter control struct, including a possible path allocated -+ * at the end of the struct. -+ */ -+static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) -+{ -+ struct autofs_dev_ioctl tmp, *ads; -+ -+ if (copy_from_user(&tmp, in, sizeof(tmp))) -+ return ERR_PTR(-EFAULT); -+ -+ if (tmp.size < sizeof(tmp)) -+ return ERR_PTR(-EINVAL); -+ -+ ads = kmalloc(tmp.size, GFP_KERNEL); -+ if (!ads) -+ return ERR_PTR(-ENOMEM); -+ -+ if (copy_from_user(ads, in, tmp.size)) { -+ kfree(ads); -+ return ERR_PTR(-EFAULT); -+ } -+ -+ return ads; -+} -+ -+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) -+{ -+ kfree(param); -+ return; -+} -+ -+/* -+ * Check sanity of parameter control fields and if a path is present -+ * check that it is terminated and contains at least one "/". -+ */ -+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) -+{ -+ int err; -+ -+ if ((err = check_dev_ioctl_version(cmd, param))) { -+ AUTOFS_WARN("invalid device control module version " -+ "supplied for cmd(0x%08x)", cmd); -+ goto out; -+ } -+ -+ if (param->size > sizeof(*param)) { -+ err = invalid_str(param->path, -+ (void *) ((size_t) param + param->size)); -+ if (err) { -+ AUTOFS_WARN( -+ "path string terminator missing for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ -+ err = check_name(param->path); -+ if (err) { -+ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", -+ cmd); -+ goto out; -+ } -+ } -+ -+ err = 0; -+out: -+ return err; -+} -+ -+/* -+ * Get the autofs super block info struct from the file opened on -+ * the autofs mount point. -+ */ -+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) -+{ -+ struct autofs_sb_info *sbi = NULL; -+ struct inode *inode; -+ -+ if (f) { -+ inode = f->f_path.dentry->d_inode; -+ sbi = autofs4_sbi(inode->i_sb); -+ } -+ return sbi; -+} -+ -+/* Return autofs module protocol version */ -+static int autofs_dev_ioctl_protover(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protover.version = sbi->version; -+ return 0; -+} -+ -+/* Return autofs module protocol sub version */ -+static int autofs_dev_ioctl_protosubver(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->protosubver.sub_version = sbi->sub_version; -+ return 0; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested device number (aka. new_encode_dev(sb->s_dev). -+ */ -+static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) -+{ -+ struct dentry *dentry; -+ struct inode *inode; -+ struct super_block *sb; -+ dev_t s_dev; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ inode = nd->path.dentry->d_inode; -+ if (!inode) -+ break; -+ -+ sb = inode->i_sb; -+ s_dev = new_encode_dev(sb->s_dev); -+ if (devno == s_dev) { -+ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { -+ err = 0; -+ break; -+ } -+ } -+ } -+out: -+ return err; -+} -+ -+/* -+ * Walk down the mount stack looking for an autofs mount that -+ * has the requested mount type (ie. indirect, direct or offset). -+ */ -+static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) -+{ -+ struct dentry *dentry; -+ struct autofs_info *ino; -+ unsigned int err; -+ -+ err = -ENOENT; -+ -+ /* Lookup the dentry name at the base of our mount point */ -+ dentry = d_lookup(nd->path.dentry, &nd->last); -+ if (!dentry) -+ goto out; -+ -+ dput(nd->path.dentry); -+ nd->path.dentry = dentry; -+ -+ /* And follow the mount stack looking for our autofs mount */ -+ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { -+ ino = autofs4_dentry_ino(nd->path.dentry); -+ if (ino && ino->sbi->type & type) { -+ err = 0; -+ break; -+ } -+ } -+out: -+ return err; -+} -+ -+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); -+ FD_SET(fd, fdt->close_on_exec); -+ 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)). -+ */ -+static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) -+{ -+ struct file *filp; -+ struct nameidata nd; -+ int err, fd; -+ -+ fd = get_unused_fd(); -+ if (likely(fd >= 0)) { -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ /* -+ * Search down, within the parent, looking for an -+ * autofs super block that has the device number -+ * corresponding to the autofs fs we want to open. -+ */ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) { -+ path_put(&nd.path); -+ goto out; -+ } -+ -+ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); -+ if (IS_ERR(filp)) { -+ err = PTR_ERR(filp); -+ goto out; -+ } -+ -+ autofs_dev_ioctl_fd_install(fd, filp); -+ } -+ -+ return fd; -+ -+out: -+ put_unused_fd(fd); -+ return err; -+} -+ -+/* Open a file descriptor on an autofs mount point */ -+static int autofs_dev_ioctl_openmount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ const char *path; -+ dev_t devid; -+ int err, fd; -+ -+ /* param->path has already been checked */ -+ if (!param->openmount.devid) -+ return -EINVAL; -+ -+ param->ioctlfd = -1; -+ -+ path = param->path; -+ devid = param->openmount.devid; -+ -+ err = 0; -+ fd = autofs_dev_ioctl_open_mountpoint(path, devid); -+ if (unlikely(fd < 0)) { -+ err = fd; -+ goto out; -+ } -+ -+ param->ioctlfd = fd; -+out: -+ return err; -+} -+ -+/* Close file descriptor allocated above (user can also use close(2)). */ -+static int autofs_dev_ioctl_closemount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ return sys_close(param->ioctlfd); -+} -+ -+/* -+ * Send "ready" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_ready(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ -+ token = (autofs_wqt_t) param->ready.token; -+ return autofs4_wait_release(sbi, token, 0); -+} -+ -+/* -+ * Send "fail" status for an existing wait (either a mount or an expire -+ * request). -+ */ -+static int autofs_dev_ioctl_fail(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs_wqt_t token; -+ int status; -+ -+ token = (autofs_wqt_t) param->fail.token; -+ status = param->fail.status ? param->fail.status : -ENOENT; -+ return autofs4_wait_release(sbi, token, status); -+} -+ -+/* -+ * Set the pipe fd for kernel communication to the daemon. -+ * -+ * Normally this is set at mount using an option but if we -+ * are reconnecting to a busy mount then we need to use this -+ * to tell the autofs mount about the new kernel pipe fd. In -+ * order to protect mounts against incorrectly setting the -+ * pipefd we also require that the autofs mount be catatonic. -+ * -+ * This also sets the process group id used to identify the -+ * controlling process (eg. the owning automount(8) daemon). -+ */ -+static int autofs_dev_ioctl_setpipefd(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ int pipefd; -+ int err = 0; -+ -+ if (param->setpipefd.pipefd == -1) -+ return -EINVAL; -+ -+ pipefd = param->setpipefd.pipefd; -+ -+ mutex_lock(&sbi->wq_mutex); -+ if (!sbi->catatonic) { -+ mutex_unlock(&sbi->wq_mutex); -+ return -EBUSY; -+ } else { -+ struct file *pipe = fget(pipefd); -+ if (!pipe->f_op || !pipe->f_op->write) { -+ err = -EPIPE; -+ fput(pipe); -+ goto out; -+ } -+ sbi->oz_pgrp = task_pgrp_nr(current); -+ sbi->pipefd = pipefd; -+ sbi->pipe = pipe; -+ sbi->catatonic = 0; -+ } -+out: -+ mutex_unlock(&sbi->wq_mutex); -+ return err; -+} -+ -+/* -+ * Make the autofs mount point catatonic, no longer responsive to -+ * mount requests. Also closes the kernel pipe file descriptor. -+ */ -+static int autofs_dev_ioctl_catatonic(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ autofs4_catatonic_mode(sbi); -+ return 0; -+} -+ -+/* Set the autofs mount timeout */ -+static int autofs_dev_ioctl_timeout(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ unsigned long timeout; -+ -+ timeout = param->timeout.timeout; -+ param->timeout.timeout = sbi->exp_timeout / HZ; -+ sbi->exp_timeout = timeout * HZ; -+ return 0; -+} -+ -+/* -+ * Return the uid and gid of the last request for the mount -+ * -+ * When reconstructing an autofs mount tree with active mounts -+ * we need to re-connect to mounts that may have used the original -+ * process uid and gid (or string variations of them) for mount -+ * lookups within the map entry. -+ */ -+static int autofs_dev_ioctl_requester(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct autofs_info *ino; -+ struct nameidata nd; -+ const char *path; -+ dev_t devid; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ devid = sbi->sb->s_dev; -+ -+ param->requester.uid = param->requester.gid = -1; -+ -+ /* Get nameidata of the parent directory */ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, devid); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ if (ino) { -+ err = 0; -+ autofs4_expire_wait(nd.path.dentry); -+ spin_lock(&sbi->fs_lock); -+ param->requester.uid = ino->uid; -+ param->requester.gid = ino->gid; -+ spin_unlock(&sbi->fs_lock); -+ } -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ * more that can be done. -+ */ -+static int autofs_dev_ioctl_expire(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct vfsmount *mnt; -+ int how; -+ -+ how = param->expire.how; -+ mnt = fp->f_path.mnt; -+ -+ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); -+} -+ -+/* Check if autofs mount point is in use */ -+static int autofs_dev_ioctl_askumount(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ param->askumount.may_umount = 0; -+ if (may_umount(fp->f_path.mnt)) -+ param->askumount.may_umount = 1; -+ return 0; -+} -+ -+/* -+ * Check if the given path is a mountpoint. -+ * -+ * If we are supplied with the file descriptor of an autofs -+ * mount we're looking for a specific mount. In this case -+ * the path is considered a mountpoint if it is itself a -+ * mountpoint or contains a mount, such as a multi-mount -+ * without a root mount. In this case we return 1 if the -+ * path is a mount point and the super magic of the covering -+ * mount if there is one or 0 if it isn't a mountpoint. -+ * -+ * If we aren't supplied with a file descriptor then we -+ * lookup the nameidata of the path and check if it is the -+ * root of a mount. If a type is given we are looking for -+ * a particular autofs mount and if we don't find a match -+ * we return fail. If the located nameidata path is the -+ * root of a mount we return 1 along with the super magic -+ * of the mount or 0 otherwise. -+ * -+ * In both cases the the device number (as returned by -+ * new_encode_dev()) is also returned. -+ */ -+static int autofs_dev_ioctl_ismountpoint(struct file *fp, -+ struct autofs_sb_info *sbi, -+ struct autofs_dev_ioctl *param) -+{ -+ struct nameidata nd; -+ const char *path; -+ unsigned int type; -+ unsigned int devid, magic; -+ int err = -ENOENT; -+ -+ if (param->size <= sizeof(*param)) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ path = param->path; -+ type = param->ismountpoint.in.type; -+ -+ param->ismountpoint.out.devid = devid = 0; -+ param->ismountpoint.out.magic = magic = 0; -+ -+ if (!fp || param->ioctlfd == -1) { -+ if (autofs_type_any(type)) { -+ struct super_block *sb; -+ -+ err = path_lookup(path, LOOKUP_FOLLOW, &nd); -+ if (err) -+ goto out; -+ -+ sb = nd.path.dentry->d_sb; -+ devid = new_encode_dev(sb->s_dev); -+ } else { -+ struct autofs_info *ino; -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_sbi_type(&nd, type); -+ if (err) -+ goto out_release; -+ -+ ino = autofs4_dentry_ino(nd.path.dentry); -+ devid = autofs4_get_dev(ino->sbi); -+ } -+ -+ err = 0; -+ if (nd.path.dentry->d_inode && -+ nd.path.mnt->mnt_root == nd.path.dentry) { -+ err = 1; -+ magic = nd.path.dentry->d_inode->i_sb->s_magic; -+ } -+ } else { -+ dev_t dev = autofs4_get_dev(sbi); -+ -+ err = path_lookup(path, LOOKUP_PARENT, &nd); -+ if (err) -+ goto out; -+ -+ err = autofs_dev_ioctl_find_super(&nd, dev); -+ if (err) -+ goto out_release; -+ -+ devid = dev; -+ -+ err = have_submounts(nd.path.dentry); -+ -+ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { -+ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { -+ struct inode *inode = nd.path.dentry->d_inode; -+ magic = inode->i_sb->s_magic; -+ } -+ } -+ } -+ -+ param->ismountpoint.out.devid = devid; -+ param->ismountpoint.out.magic = magic; -+ -+out_release: -+ path_put(&nd.path); -+out: -+ return err; -+} -+ -+/* -+ * Our range of ioctl numbers isn't 0 based so we need to shift -+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table -+ * lookup. -+ */ -+#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) -+ -+static ioctl_fn lookup_dev_ioctl(unsigned int cmd) -+{ -+ static struct { -+ int cmd; -+ ioctl_fn fn; -+ } _ioctls[] = { -+ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), -+ autofs_dev_ioctl_protover}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), -+ autofs_dev_ioctl_protosubver}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), -+ autofs_dev_ioctl_openmount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), -+ autofs_dev_ioctl_closemount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), -+ autofs_dev_ioctl_ready}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), -+ autofs_dev_ioctl_fail}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), -+ autofs_dev_ioctl_setpipefd}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), -+ autofs_dev_ioctl_catatonic}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), -+ autofs_dev_ioctl_timeout}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), -+ autofs_dev_ioctl_requester}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), -+ autofs_dev_ioctl_expire}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), -+ autofs_dev_ioctl_askumount}, -+ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), -+ autofs_dev_ioctl_ismountpoint} -+ }; -+ unsigned int idx = cmd_idx(cmd); -+ -+ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; -+} -+ -+/* ioctl dispatcher */ -+static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) -+{ -+ struct autofs_dev_ioctl *param; -+ struct file *fp; -+ struct autofs_sb_info *sbi; -+ unsigned int cmd_first, cmd; -+ ioctl_fn fn = NULL; -+ int err = 0; -+ -+ /* only root can play with this */ -+ if (!capable(CAP_SYS_ADMIN)) -+ return -EPERM; -+ -+ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); -+ cmd = _IOC_NR(command); -+ -+ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || -+ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { -+ return -ENOTTY; -+ } -+ -+ /* Copy the parameters into kernel space. */ -+ param = copy_dev_ioctl(user); -+ if (IS_ERR(param)) -+ return PTR_ERR(param); -+ -+ err = validate_dev_ioctl(command, param); -+ if (err) -+ goto out; -+ -+ /* The validate routine above always sets the version */ -+ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) -+ goto done; -+ -+ fn = lookup_dev_ioctl(cmd); -+ if (!fn) { -+ AUTOFS_WARN("unknown command 0x%08x", command); -+ return -ENOTTY; -+ } -+ -+ fp = NULL; -+ sbi = NULL; -+ -+ /* -+ * For obvious reasons the openmount can't have a file -+ * descriptor yet. We don't take a reference to the -+ * file during close to allow for immediate release. -+ */ -+ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && -+ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { -+ fp = fget(param->ioctlfd); -+ if (!fp) { -+ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) -+ goto cont; -+ err = -EBADF; -+ goto out; -+ } -+ -+ if (!fp->f_op) { -+ err = -ENOTTY; -+ fput(fp); -+ goto out; -+ } -+ -+ sbi = autofs_dev_ioctl_sbi(fp); -+ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { -+ err = -EINVAL; -+ fput(fp); -+ goto out; -+ } -+ -+ /* -+ * Admin needs to be able to set the mount catatonic in -+ * order to be able to perform the re-open. -+ */ -+ if (!autofs4_oz_mode(sbi) && -+ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { -+ err = -EACCES; -+ fput(fp); -+ goto out; -+ } -+ } -+cont: -+ err = fn(fp, sbi, param); -+ -+ if (fp) -+ fput(fp); -+done: -+ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) -+ err = -EFAULT; -+out: -+ free_dev_ioctl(param); -+ return err; -+} -+ -+static long autofs_dev_ioctl(struct file *file, uint command, ulong u) -+{ -+ int err; -+ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); -+ return (long) err; -+} -+ -+#ifdef CONFIG_COMPAT -+static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) -+{ -+ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); -+} -+#else -+#define autofs_dev_ioctl_compat NULL -+#endif -+ -+static const struct file_operations _dev_ioctl_fops = { -+ .unlocked_ioctl = autofs_dev_ioctl, -+ .compat_ioctl = autofs_dev_ioctl_compat, -+ .owner = THIS_MODULE, -+}; -+ -+static struct miscdevice _autofs_dev_ioctl_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = AUTOFS_DEVICE_NAME, -+ .fops = &_dev_ioctl_fops -+}; -+ -+/* Register/deregister misc character device */ -+int autofs_dev_ioctl_init(void) -+{ -+ int r; -+ -+ r = misc_register(&_autofs_dev_ioctl_misc); -+ if (r) { -+ AUTOFS_ERROR("misc_register failed for control device"); -+ return r; -+ } -+ -+ return 0; -+} -+ -+void autofs_dev_ioctl_exit(void) -+{ -+ misc_deregister(&_autofs_dev_ioctl_misc); -+ return; -+} -+ -Index: linux-2.6.27/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/expire.c -+++ linux-2.6.27/fs/autofs4/expire.c -@@ -63,7 +63,7 @@ static int autofs4_mount_busy(struct vfs - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - - /* This is an autofs submount, we can't expire it */ -- if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ if (autofs_type_indirect(sbi->type)) - goto done; - - /* -@@ -255,10 +255,10 @@ cont: - } - - /* Check if we can expire a direct mount (possibly a tree) */ --static struct dentry *autofs4_expire_direct(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = dget(sb->s_root); -@@ -294,10 +294,10 @@ static struct dentry *autofs4_expire_dir - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire_indirect(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -478,22 +478,16 @@ int autofs4_expire_run(struct super_bloc - return ret; - } - --/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -- more to be done */ --int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -- struct autofs_sb_info *sbi, int __user *arg) -+int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int when) - { - struct dentry *dentry; - int ret = -EAGAIN; -- int do_now = 0; - -- if (arg && get_user(do_now, arg)) -- return -EFAULT; -- -- if (sbi->type & AUTOFS_TYPE_TRIGGER) -- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ if (autofs_type_trigger(sbi->type)) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, when); - else -- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); - - if (dentry) { - struct autofs_info *ino = autofs4_dentry_ino(dentry); -@@ -516,3 +510,16 @@ int autofs4_expire_multi(struct super_bl - return ret; - } - -+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing -+ more to be done */ -+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, int __user *arg) -+{ -+ int do_now = 0; -+ -+ if (arg && get_user(do_now, arg)) -+ return -EFAULT; -+ -+ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); -+} -+ -Index: linux-2.6.27/fs/autofs4/init.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/init.c -+++ linux-2.6.27/fs/autofs4/init.c -@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs - - static int __init init_autofs4_fs(void) - { -- return register_filesystem(&autofs_fs_type); -+ int err; -+ -+ err = register_filesystem(&autofs_fs_type); -+ if (err) -+ return err; -+ -+ autofs_dev_ioctl_init(); -+ -+ return err; - } - - static void __exit exit_autofs4_fs(void) - { -+ autofs_dev_ioctl_exit(); - unregister_filesystem(&autofs_fs_type); - } - -Index: linux-2.6.27/include/linux/auto_dev-ioctl.h -=================================================================== ---- /dev/null -+++ linux-2.6.27/include/linux/auto_dev-ioctl.h -@@ -0,0 +1,224 @@ -+/* -+ * Copyright 2008 Red Hat, Inc. All rights reserved. -+ * Copyright 2008 Ian Kent -+ * -+ * This file is part of the Linux kernel and is made available under -+ * the terms of the GNU General Public License, version 2, or at your -+ * option, any later version, incorporated herein by reference. -+ */ -+ -+#ifndef _LINUX_AUTO_DEV_IOCTL_H -+#define _LINUX_AUTO_DEV_IOCTL_H -+ -+#include -+#include -+ -+#define AUTOFS_DEVICE_NAME "autofs" -+ -+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 -+ -+#define AUTOFS_DEVID_LEN 16 -+ -+#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) -+ -+/* -+ * An ioctl interface for autofs mount point control. -+ */ -+ -+struct args_protover { -+ __u32 version; -+}; -+ -+struct args_protosubver { -+ __u32 sub_version; -+}; -+ -+struct args_openmount { -+ __u32 devid; -+}; -+ -+struct args_ready { -+ __u32 token; -+}; -+ -+struct args_fail { -+ __u32 token; -+ __s32 status; -+}; -+ -+struct args_setpipefd { -+ __s32 pipefd; -+}; -+ -+struct args_timeout { -+ __u64 timeout; -+}; -+ -+struct args_requester { -+ __u32 uid; -+ __u32 gid; -+}; -+ -+struct args_expire { -+ __u32 how; -+}; -+ -+struct args_askumount { -+ __u32 may_umount; -+}; -+ -+struct args_ismountpoint { -+ union { -+ struct args_in { -+ __u32 type; -+ } in; -+ struct args_out { -+ __u32 devid; -+ __u32 magic; -+ } out; -+ }; -+}; -+ -+/* -+ * All the ioctls use this structure. -+ * When sending a path size must account for the total length -+ * of the chunk of memory otherwise is is the size of the -+ * structure. -+ */ -+ -+struct autofs_dev_ioctl { -+ __u32 ver_major; -+ __u32 ver_minor; -+ __u32 size; /* total size of data passed in -+ * including this struct */ -+ __s32 ioctlfd; /* automount command fd */ -+ -+ /* Command parameters */ -+ -+ union { -+ struct args_protover protover; -+ struct args_protosubver protosubver; -+ struct args_openmount openmount; -+ struct args_ready ready; -+ struct args_fail fail; -+ struct args_setpipefd setpipefd; -+ struct args_timeout timeout; -+ struct args_requester requester; -+ struct args_expire expire; -+ struct args_askumount askumount; -+ struct args_ismountpoint ismountpoint; -+ }; -+ -+ char path[0]; -+}; -+ -+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) -+{ -+ memset(in, 0, sizeof(struct autofs_dev_ioctl)); -+ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; -+ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; -+ in->size = sizeof(struct autofs_dev_ioctl); -+ in->ioctlfd = -1; -+ return; -+} -+ -+/* -+ * If you change this make sure you make the corresponding change -+ * to autofs-dev-ioctl.c:lookup_ioctl() -+ */ -+enum { -+ /* Get various version info */ -+ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, -+ -+ /* Open mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, -+ -+ /* Close mount ioctl fd */ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, -+ -+ /* Mount/expire status returns */ -+ AUTOFS_DEV_IOCTL_READY_CMD, -+ AUTOFS_DEV_IOCTL_FAIL_CMD, -+ -+ /* Activate/deactivate autofs mount */ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, -+ -+ /* Expiry timeout */ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, -+ -+ /* Get mount last requesting uid and gid */ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, -+ -+ /* Check for eligible expire candidates */ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, -+ -+ /* Request busy status */ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, -+ -+ /* Check if path is a mountpoint */ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -+}; -+ -+#define AUTOFS_IOCTL 0x93 -+ -+#define AUTOFS_DEV_IOCTL_VERSION \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_OPENMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_READY \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_FAIL \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_SETPIPEFD \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_CATATONIC \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_TIMEOUT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_REQUESTER \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_EXPIRE \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) -+ -+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ -+ _IOWR(AUTOFS_IOCTL, \ -+ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) -+ -+#endif /* _LINUX_AUTO_DEV_IOCTL_H */ -Index: linux-2.6.27/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.27.orig/include/linux/auto_fs4.h -+++ linux-2.6.27/include/linux/auto_fs4.h -@@ -23,16 +23,70 @@ - #define AUTOFS_MIN_PROTO_VERSION 3 - #define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 0 -+#define AUTOFS_PROTO_SUBVERSION 1 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --#define AUTOFS_TYPE_ANY 0x0000 --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -+#define AUTOFS_TYPE_ANY 0U -+#define AUTOFS_TYPE_INDIRECT 1U -+#define AUTOFS_TYPE_DIRECT 2U -+#define AUTOFS_TYPE_OFFSET 4U -+ -+static inline void set_autofs_type_indirect(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_INDIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_indirect(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_INDIRECT); -+} -+ -+static inline void set_autofs_type_direct(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_DIRECT; -+ return; -+} -+ -+static inline unsigned int autofs_type_direct(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT); -+} -+ -+static inline void set_autofs_type_offset(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_OFFSET; -+ return; -+} -+ -+static inline unsigned int autofs_type_offset(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_OFFSET); -+} -+ -+static inline unsigned int autofs_type_trigger(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); -+} -+ -+/* -+ * This isn't really a type as we use it to say "no type set" to -+ * indicate we want to search for "any" mount in the -+ * autofs_dev_ioctl_ismountpoint() device ioctl function. -+ */ -+static inline void set_autofs_type_any(unsigned int *type) -+{ -+ *type = AUTOFS_TYPE_ANY; -+ return; -+} -+ -+static inline unsigned int autofs_type_any(unsigned int type) -+{ -+ return (type == AUTOFS_TYPE_ANY); -+} - - /* Daemon notification packet types */ - enum autofs_notify { diff --git a/patches/autofs4-2.6.27-v5-update-20081027.patch b/patches/autofs4-2.6.27-v5-update-20081027.patch deleted file mode 100644 index 80b9753..0000000 --- a/patches/autofs4-2.6.27-v5-update-20081027.patch +++ /dev/null @@ -1,148 +0,0 @@ -Index: linux-2.6.27/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.27/fs/autofs4/autofs_i.h -@@ -21,6 +21,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -92,10 +94,6 @@ struct autofs_wait_queue { - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - --#define AUTOFS_TYPE_INDIRECT 0x0001 --#define AUTOFS_TYPE_DIRECT 0x0002 --#define AUTOFS_TYPE_OFFSET 0x0004 -- - struct autofs_sb_info { - u32 magic; - int pipefd; -Index: linux-2.6.27/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/expire.c -+++ linux-2.6.27/fs/autofs4/expire.c -@@ -56,12 +56,23 @@ static int autofs4_mount_busy(struct vfs - mntget(mnt); - dget(dentry); - -- if (!autofs4_follow_mount(&mnt, &dentry)) -+ if (!follow_down(&mnt, &dentry)) - goto done; - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -- goto done; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - - /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(mnt)) { -@@ -479,7 +490,7 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if (sbi->type & AUTOFS_TYPE_DIRECT) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); - else - dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -Index: linux-2.6.27/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/inode.c -+++ linux-2.6.27/fs/autofs4/inode.c -@@ -288,7 +288,7 @@ static int parse_options(char *options, - *type = AUTOFS_TYPE_DIRECT; - break; - case Opt_offset: -- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; -+ *type = AUTOFS_TYPE_OFFSET; - break; - default: - return 1; -@@ -336,7 +336,7 @@ int autofs4_fill_super(struct super_bloc - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -- sbi->type = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); -@@ -378,7 +378,7 @@ int autofs4_fill_super(struct super_bloc - } - - root_inode->i_fop = &autofs4_root_operations; -- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? - &autofs4_direct_root_inode_operations : - &autofs4_indirect_root_inode_operations; - -Index: linux-2.6.27/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.27.orig/fs/autofs4/waitq.c -+++ linux-2.6.27/fs/autofs4/waitq.c -@@ -337,7 +337,7 @@ int autofs4_wait(struct autofs_sb_info * - * is very similar for indirect mounts except only dentrys - * in the root of the autofs file system may be negative. - */ -- if (sbi->type & (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET)) -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) - return -ENOENT; - else if (!IS_ROOT(dentry->d_parent)) - return -ENOENT; -@@ -348,7 +348,7 @@ int autofs4_wait(struct autofs_sb_info * - return -ENOMEM; - - /* If this is a direct mount request create a dummy name */ -- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) - qstr.len = sprintf(name, "%p", dentry); - else { - qstr.len = autofs4_getpath(sbi, dentry, &name); -@@ -406,11 +406,11 @@ int autofs4_wait(struct autofs_sb_info * - type = autofs_ptype_expire_multi; - } else { - if (notify == NFY_MOUNT) -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_missing_direct : - autofs_ptype_missing_indirect; - else -- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? - autofs_ptype_expire_direct : - autofs_ptype_expire_indirect; - } -Index: linux-2.6.27/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.27.orig/include/linux/auto_fs4.h -+++ linux-2.6.27/include/linux/auto_fs4.h -@@ -29,6 +29,11 @@ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ - /* Daemon notification packet types */ - enum autofs_notify { - NFY_NONE, diff --git a/patches/autofs4-2.6.27-v5-update-20090903.patch b/patches/autofs4-2.6.27-v5-update-20090903.patch new file mode 100644 index 0000000..d0d92e0 --- /dev/null +++ b/patches/autofs4-2.6.27-v5-update-20090903.patch @@ -0,0 +1,2041 @@ +--- linux-2.6.27.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.27/fs/autofs4/autofs_i.h +@@ -14,6 +14,7 @@ + /* Internal header file for autofs */ + + #include ++#include + #include + #include + +@@ -21,6 +22,9 @@ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY + #define AUTOFS_IOC_COUNT 32 + ++#define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) ++#define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) ++ + #include + #include + #include +@@ -35,11 +39,27 @@ + /* #define DEBUG */ + + #ifdef DEBUG +-#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __func__ , ##args); } while(0) ++#define DPRINTK(fmt, args...) \ ++do { \ ++ printk(KERN_DEBUG "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) + #else +-#define DPRINTK(fmt,args...) do {} while(0) ++#define DPRINTK(fmt, args...) do {} while (0) + #endif + ++#define AUTOFS_WARN(fmt, args...) \ ++do { \ ++ printk(KERN_WARNING "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) ++ ++#define AUTOFS_ERROR(fmt, args...) \ ++do { \ ++ printk(KERN_ERR "pid %d: %s: " fmt "\n", \ ++ current->pid, __func__, ##args); \ ++} while (0) ++ + /* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never +@@ -61,6 +81,9 @@ struct autofs_info { + unsigned long last_used; + atomic_t count; + ++ uid_t uid; ++ gid_t gid; ++ + mode_t mode; + size_t size; + +@@ -92,10 +115,6 @@ struct autofs_wait_queue { + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 +- + struct autofs_sb_info { + u32 magic; + int pipefd; +@@ -167,8 +186,21 @@ int autofs4_expire_wait(struct dentry *d + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int how); ++ ++/* Device node initialization */ ++ ++int autofs_dev_ioctl_init(void); ++void autofs_dev_ioctl_exit(void); + + /* Operations structures */ + +--- linux-2.6.27.orig/fs/autofs4/expire.c ++++ linux-2.6.27/fs/autofs4/expire.c +@@ -56,12 +56,25 @@ static int autofs4_mount_busy(struct vfs + mntget(mnt); + dget(dentry); + +- if (!autofs4_follow_mount(&mnt, &dentry)) ++ if (!follow_down(&mnt, &dentry)) + goto done; + +- /* This is an autofs submount, we can't expire it */ +- if (is_autofs4_dentry(dentry)) +- goto done; ++ if (is_autofs4_dentry(dentry)) { ++ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); ++ ++ /* This is an autofs submount, we can't expire it */ ++ if (autofs_type_indirect(sbi->type)) ++ goto done; ++ ++ /* ++ * Otherwise it's an offset mount and we need to check ++ * if we can umount its mount, if there is one. ++ */ ++ if (!d_mountpoint(dentry)) { ++ status = 0; ++ goto done; ++ } ++ } + + /* Update the expiry counter if fs is busy */ + if (!may_umount_tree(mnt)) { +@@ -244,10 +257,10 @@ cont: + } + + /* Check if we can expire a direct mount (possibly a tree) */ +-static struct dentry *autofs4_expire_direct(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_direct(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = dget(sb->s_root); +@@ -283,10 +296,10 @@ static struct dentry *autofs4_expire_dir + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +-static struct dentry *autofs4_expire_indirect(struct super_block *sb, +- struct vfsmount *mnt, +- struct autofs_sb_info *sbi, +- int how) ++struct dentry *autofs4_expire_indirect(struct super_block *sb, ++ struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, ++ int how) + { + unsigned long timeout; + struct dentry *root = sb->s_root; +@@ -467,22 +480,16 @@ int autofs4_expire_run(struct super_bloc + return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_DIRECT) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); +@@ -505,3 +512,16 @@ int autofs4_expire_multi(struct super_bl + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.27.orig/fs/autofs4/inode.c ++++ linux-2.6.27/fs/autofs4/inode.c +@@ -53,6 +53,8 @@ struct autofs_info *autofs4_init_ino(str + atomic_set(&ino->count, 0); + } + ++ ino->uid = 0; ++ ino->gid = 0; + ino->mode = mode; + ino->last_used = jiffies; + +@@ -195,9 +197,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -282,13 +284,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -336,7 +338,7 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = 0; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); +@@ -378,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.27.orig/fs/autofs4/waitq.c ++++ linux-2.6.27/fs/autofs4/waitq.c +@@ -297,20 +297,14 @@ static int validate_request(struct autof + */ + if (notify == NFY_MOUNT) { + /* +- * If the dentry isn't hashed just go ahead and try the +- * mount again with a new wait (not much else we can do). +- */ +- if (!d_unhashed(dentry)) { +- /* +- * But if the dentry is hashed, that means that we +- * got here through the revalidate path. Thus, we +- * need to check if the dentry has been mounted +- * while we waited on the wq_mutex. If it has, +- * simply return success. +- */ +- if (d_mountpoint(dentry)) +- return 0; +- } ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) ++ return 0; + } + + return 1; +@@ -337,7 +331,7 @@ int autofs4_wait(struct autofs_sb_info * + * is very similar for indirect mounts except only dentrys + * in the root of the autofs file system may be negative. + */ +- if (sbi->type & (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET)) ++ if (autofs_type_trigger(sbi->type)) + return -ENOENT; + else if (!IS_ROOT(dentry->d_parent)) + return -ENOENT; +@@ -348,7 +342,7 @@ int autofs4_wait(struct autofs_sb_info * + return -ENOMEM; + + /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT)) ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) + qstr.len = sprintf(name, "%p", dentry); + else { + qstr.len = autofs4_getpath(sbi, dentry, &name); +@@ -406,11 +400,11 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } +@@ -457,6 +451,40 @@ int autofs4_wait(struct autofs_sb_info * + + status = wq->status; + ++ /* ++ * For direct and offset mounts we need to track the requester's ++ * uid and gid in the dentry info struct. This is so it can be ++ * supplied, on request, by the misc device ioctl interface. ++ * This is needed during daemon resatart when reconnecting ++ * to existing, active, autofs mounts. The uid and gid (and ++ * related string values) may be used for macro substitution ++ * in autofs mount maps. ++ */ ++ if (!status) { ++ struct autofs_info *ino; ++ struct dentry *de = NULL; ++ ++ /* direct mount or browsable map */ ++ ino = autofs4_dentry_ino(dentry); ++ if (!ino) { ++ /* If not lookup actual dentry used */ ++ de = d_lookup(dentry->d_parent, &dentry->d_name); ++ if (de) ++ ino = autofs4_dentry_ino(de); ++ } ++ ++ /* Set mount requester */ ++ if (ino) { ++ spin_lock(&sbi->fs_lock); ++ ino->uid = wq->uid; ++ ino->gid = wq->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++ if (de) ++ dput(de); ++ } ++ + /* Are we the last process to need status? */ + mutex_lock(&sbi->wq_mutex); + if (!--wq->wait_ctr) +--- linux-2.6.27.orig/include/linux/auto_fs4.h ++++ linux-2.6.27/include/linux/auto_fs4.h +@@ -23,12 +23,71 @@ + #define AUTOFS_MIN_PROTO_VERSION 3 + #define AUTOFS_MAX_PROTO_VERSION 5 + +-#define AUTOFS_PROTO_SUBVERSION 0 ++#define AUTOFS_PROTO_SUBVERSION 1 + + /* Mask for expire behaviour */ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} ++ + /* Daemon notification packet types */ + enum autofs_notify { + NFY_NONE, +--- /dev/null ++++ linux-2.6.27/Documentation/filesystems/autofs4-mount-control.txt +@@ -0,0 +1,414 @@ ++ ++Miscellaneous Device control operations for the autofs4 kernel module ++==================================================================== ++ ++The problem ++=========== ++ ++There is a problem with active restarts in autofs (that is to say ++restarting autofs when there are busy mounts). ++ ++During normal operation autofs uses a file descriptor opened on the ++directory that is being managed in order to be able to issue control ++operations. Using a file descriptor gives ioctl operations access to ++autofs specific information stored in the super block. The operations ++are things such as setting an autofs mount catatonic, setting the ++expire timeout and requesting expire checks. As is explained below, ++certain types of autofs triggered mounts can end up covering an autofs ++mount itself which prevents us being able to use open(2) to obtain a ++file descriptor for these operations if we don't already have one open. ++ ++Currently autofs uses "umount -l" (lazy umount) to clear active mounts ++at restart. While using lazy umount works for most cases, anything that ++needs to walk back up the mount tree to construct a path, such as ++getcwd(2) and the proc file system /proc//cwd, no longer works ++because the point from which the path is constructed has been detached ++from the mount tree. ++ ++The actual problem with autofs is that it can't reconnect to existing ++mounts. Immediately one thinks of just adding the ability to remount ++autofs file systems would solve it, but alas, that can't work. This is ++because autofs direct mounts and the implementation of "on demand mount ++and expire" of nested mount trees have the file system mounted directly ++on top of the mount trigger directory dentry. ++ ++For example, there are two types of automount maps, direct (in the kernel ++module source you will see a third type called an offset, which is just ++a direct mount in disguise) and indirect. ++ ++Here is a master map with direct and indirect map entries: ++ ++/- /etc/auto.direct ++/test /etc/auto.indirect ++ ++and the corresponding map files: ++ ++/etc/auto.direct: ++ ++/automount/dparse/g6 budgie:/autofs/export1 ++/automount/dparse/g1 shark:/autofs/export1 ++and so on. ++ ++/etc/auto.indirect: ++ ++g1 shark:/autofs/export1 ++g6 budgie:/autofs/export1 ++and so on. ++ ++For the above indirect map an autofs file system is mounted on /test and ++mounts are triggered for each sub-directory key by the inode lookup ++operation. So we see a mount of shark:/autofs/export1 on /test/g1, for ++example. ++ ++The way that direct mounts are handled is by making an autofs mount on ++each full path, such as /automount/dparse/g1, and using it as a mount ++trigger. So when we walk on the path we mount shark:/autofs/export1 "on ++top of this mount point". Since these are always directories we can ++use the follow_link inode operation to trigger the mount. ++ ++But, each entry in direct and indirect maps can have offsets (making ++them multi-mount map entries). ++ ++For example, an indirect mount map entry could also be: ++ ++g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export1 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++and a similarly a direct mount map entry could also be: ++ ++/automount/dparse/g1 \ ++ / shark:/autofs/export5/testing/test \ ++ /s1 shark:/autofs/export/testing/test/s1 \ ++ /s2 shark:/autofs/export5/testing/test/s2 \ ++ /s1/ss1 shark:/autofs/export2 \ ++ /s2/ss2 shark:/autofs/export2 ++ ++One of the issues with version 4 of autofs was that, when mounting an ++entry with a large number of offsets, possibly with nesting, we needed ++to mount and umount all of the offsets as a single unit. Not really a ++problem, except for people with a large number of offsets in map entries. ++This mechanism is used for the well known "hosts" map and we have seen ++cases (in 2.4) where the available number of mounts are exhausted or ++where the number of privileged ports available is exhausted. ++ ++In version 5 we mount only as we go down the tree of offsets and ++similarly for expiring them which resolves the above problem. There is ++somewhat more detail to the implementation but it isn't needed for the ++sake of the problem explanation. The one important detail is that these ++offsets are implemented using the same mechanism as the direct mounts ++above and so the mount points can be covered by a mount. ++ ++The current autofs implementation uses an ioctl file descriptor opened ++on the mount point for control operations. The references held by the ++descriptor are accounted for in checks made to determine if a mount is ++in use and is also used to access autofs file system information held ++in the mount super block. So the use of a file handle needs to be ++retained. ++ ++ ++The Solution ++============ ++ ++To be able to restart autofs leaving existing direct, indirect and ++offset mounts in place we need to be able to obtain a file handle ++for these potentially covered autofs mount points. Rather than just ++implement an isolated operation it was decided to re-implement the ++existing ioctl interface and add new operations to provide this ++functionality. ++ ++In addition, to be able to reconstruct a mount tree that has busy mounts, ++the uid and gid of the last user that triggered the mount needs to be ++available because these can be used as macro substitution variables in ++autofs maps. They are recorded at mount request time and an operation ++has been added to retrieve them. ++ ++Since we're re-implementing the control interface, a couple of other ++problems with the existing interface have been addressed. First, when ++a mount or expire operation completes a status is returned to the ++kernel by either a "send ready" or a "send fail" operation. The ++"send fail" operation of the ioctl interface could only ever send ++ENOENT so the re-implementation allows user space to send an actual ++status. Another expensive operation in user space, for those using ++very large maps, is discovering if a mount is present. Usually this ++involves scanning /proc/mounts and since it needs to be done quite ++often it can introduce significant overhead when there are many entries ++in the mount table. An operation to lookup the mount status of a mount ++point dentry (covered or not) has also been added. ++ ++Current kernel development policy recommends avoiding the use of the ++ioctl mechanism in favor of systems such as Netlink. An implementation ++using this system was attempted to evaluate its suitability and it was ++found to be inadequate, in this case. The Generic Netlink system was ++used for this as raw Netlink would lead to a significant increase in ++complexity. There's no question that the Generic Netlink system is an ++elegant solution for common case ioctl functions but it's not a complete ++replacement probably because it's primary purpose in life is to be a ++message bus implementation rather than specifically an ioctl replacement. ++While it would be possible to work around this there is one concern ++that lead to the decision to not use it. This is that the autofs ++expire in the daemon has become far to complex because umount ++candidates are enumerated, almost for no other reason than to "count" ++the number of times to call the expire ioctl. This involves scanning ++the mount table which has proved to be a big overhead for users with ++large maps. The best way to improve this is try and get back to the ++way the expire was done long ago. That is, when an expire request is ++issued for a mount (file handle) we should continually call back to ++the daemon until we can't umount any more mounts, then return the ++appropriate status to the daemon. At the moment we just expire one ++mount at a time. A Generic Netlink implementation would exclude this ++possibility for future development due to the requirements of the ++message bus architecture. ++ ++ ++autofs4 Miscellaneous Device mount control interface ++==================================================== ++ ++The control interface is opening a device node, typically /dev/autofs. ++ ++All the ioctls use a common structure to pass the needed parameter ++information and return operation results: ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++The ioctlfd field is a mount point file descriptor of an autofs mount ++point. It is returned by the open call and is used by all calls except ++the check for whether a given path is a mount point, where it may ++optionally be used to check a specific mount corresponding to a given ++mount point file descriptor, and when requesting the uid and gid of the ++last successful mount on a directory within the autofs file system. ++ ++The anonymous union is used to communicate parameters and results of calls ++made as described below. ++ ++The path field is used to pass a path where it is needed and the size field ++is used account for the increased structure length when translating the ++structure sent from user space. ++ ++This structure can be initialized before setting specific fields by using ++the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *). ++ ++All of the ioctls perform a copy of this structure from user space to ++kernel space and return -EINVAL if the size parameter is smaller than ++the structure size itself, -ENOMEM if the kernel memory allocation fails ++or -EFAULT if the copy itself fails. Other checks include a version check ++of the compiled in user space version against the module version and a ++mismatch results in a -EINVAL return. If the size field is greater than ++the structure size then a path is assumed to be present and is checked to ++ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is ++returned. Following these checks, for all ioctl commands except ++AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and ++AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is ++not a valid descriptor or doesn't correspond to an autofs mount point ++an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is ++returned. ++ ++ ++The ioctls ++========== ++ ++An example of an implementation which uses this interface can be seen ++in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the ++distribution tar available for download from kernel.org in directory ++/pub/linux/daemons/autofs/v5. ++ ++The device node ioctl operations implemented by this interface are: ++ ++ ++AUTOFS_DEV_IOCTL_VERSION ++------------------------ ++ ++Get the major and minor version of the autofs4 device ioctl kernel module ++implementation. It requires an initialized struct autofs_dev_ioctl as an ++input parameter and sets the version information in the passed in structure. ++It returns 0 on success or the error -EINVAL if a version mismatch is ++detected. ++ ++ ++AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD ++------------------------------------------------------------------ ++ ++Get the major and minor version of the autofs4 protocol version understood ++by loaded module. This call requires an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to a valid autofs mount point descriptor ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. ++ ++ ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ ++ ++Obtain and release a file descriptor for an autofs managed mount point ++path. The open call requires an initialized struct autofs_dev_ioctl with ++the the path field set and the size field adjusted appropriately as well ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. ++ ++ ++AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD ++-------------------------------------------------------- ++ ++Return mount and expire result status from user space to the kernel. ++Both of these calls require an initialized struct autofs_dev_ioctl ++with the ioctlfd field set to the descriptor obtained from the open ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. ++ ++ ++AUTOFS_DEV_IOCTL_SETPIPEFD_CMD ++------------------------------ ++ ++Set the pipe file descriptor used for kernel communication to the daemon. ++Normally this is set at mount time using an option but when reconnecting ++to a existing mount we need to use this to tell the autofs mount about ++the new kernel pipe descriptor. In order to protect mounts against ++incorrectly setting the pipe descriptor we also require that the autofs ++mount be catatonic (see next call). ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. ++ ++ ++AUTOFS_DEV_IOCTL_CATATONIC_CMD ++------------------------------ ++ ++Make the autofs mount point catatonic. The autofs mount will no longer ++issue mount requests, the kernel communication pipe descriptor is released ++and any remaining waits in the queue released. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++ ++ ++AUTOFS_DEV_IOCTL_TIMEOUT_CMD ++---------------------------- ++ ++Set the expire timeout for mounts withing an autofs mount point. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. ++ ++ ++AUTOFS_DEV_IOCTL_REQUESTER_CMD ++------------------------------ ++ ++Return the uid and gid of the last process to successfully trigger a the ++mount on the given path dentry. ++ ++The call requires an initialized struct autofs_dev_ioctl with the path ++field set to the mount point in question and the size field adjusted ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. ++ ++When reconstructing an autofs mount tree with active mounts we need to ++re-connect to mounts that may have used the original process uid and ++gid (or string variations of them) for mount lookups within the map entry. ++This call provides the ability to obtain this uid and gid so they may be ++used by user space for the mount map lookups. ++ ++ ++AUTOFS_DEV_IOCTL_EXPIRE_CMD ++--------------------------- ++ ++Issue an expire request to the kernel for an autofs mount. Typically ++this ioctl is called until no further expire candidates are found. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call. In ++addition an immediate expire, independent of the mount timeout, can be ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. ++ ++This call causes the kernel module to check the mount corresponding ++to the given ioctlfd for mounts that can be expired, issues an expire ++request back to the daemon and waits for completion. ++ ++AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD ++------------------------------ ++ ++Checks if an autofs mount point is in use. ++ ++The call requires an initialized struct autofs_dev_ioctl with the ++ioctlfd field set to the descriptor obtained from the open call and ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. ++ ++ ++AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD ++--------------------------------- ++ ++Check if the given path is a mountpoint. ++ ++The call requires an initialized struct autofs_dev_ioctl. There are two ++possible variations. Both use the path field set to the path of the mount ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. ++ ++If supplied with a file descriptor we're looking for a specific mount, ++not necessarily at the top of the mounted stack. In this case the path ++the descriptor corresponds to is considered a mountpoint if it is itself ++a mountpoint or contains a mount, such as a multi-mount without a root ++mount. In this case we return 1 if the descriptor corresponds to a mount ++point and and also returns the super magic of the covering mount if there ++is one or 0 if it isn't a mountpoint. ++ ++If a path is supplied (and the ioctlfd field is set to -1) then the path ++is looked up and is checked to see if it is the root of a mount. If a ++type is also given we are looking for a particular autofs mount and if ++a match isn't found a fail is returned. If the the located path is the ++root of a mount 1 is returned along with the super magic of the mount ++or 0 otherwise. ++ +--- linux-2.6.27.orig/fs/autofs4/Makefile ++++ linux-2.6.27/fs/autofs4/Makefile +@@ -4,4 +4,4 @@ + + obj-$(CONFIG_AUTOFS4_FS) += autofs4.o + +-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o ++autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o +--- /dev/null ++++ linux-2.6.27/fs/autofs4/dev-ioctl.c +@@ -0,0 +1,841 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "autofs_i.h" ++ ++/* ++ * This module implements an interface for routing autofs ioctl control ++ * commands via a miscellaneous device file. ++ * ++ * The alternate interface is needed because we need to be able open ++ * an ioctl file descriptor on an autofs mount that may be covered by ++ * another mount. This situation arises when starting automount(8) ++ * or other user space daemon which uses direct mounts or offset ++ * mounts (used for autofs lazy mount/umount of nested mount trees), ++ * which have been left busy at at service shutdown. ++ */ ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++typedef int (*ioctl_fn)(struct file *, ++struct autofs_sb_info *, struct autofs_dev_ioctl *); ++ ++static int check_name(const char *name) ++{ ++ if (!strchr(name, '/')) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * Check a string doesn't overrun the chunk of ++ * memory we copied from user land. ++ */ ++static int invalid_str(char *str, void *end) ++{ ++ while ((void *) str <= end) ++ if (!*str++) ++ return 0; ++ return -EINVAL; ++} ++ ++/* ++ * Check that the user compiled against correct version of autofs ++ * misc device code. ++ * ++ * As well as checking the version compatibility this always copies ++ * the kernel interface version out. ++ */ ++static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err = 0; ++ ++ if ((AUTOFS_DEV_IOCTL_VERSION_MAJOR != param->ver_major) || ++ (AUTOFS_DEV_IOCTL_VERSION_MINOR < param->ver_minor)) { ++ AUTOFS_WARN("ioctl control interface version mismatch: " ++ "kernel(%u.%u), user(%u.%u), cmd(%d)", ++ AUTOFS_DEV_IOCTL_VERSION_MAJOR, ++ AUTOFS_DEV_IOCTL_VERSION_MINOR, ++ param->ver_major, param->ver_minor, cmd); ++ err = -EINVAL; ++ } ++ ++ /* Fill in the kernel version. */ ++ param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ ++ return err; ++} ++ ++/* ++ * Copy parameter control struct, including a possible path allocated ++ * at the end of the struct. ++ */ ++static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in) ++{ ++ struct autofs_dev_ioctl tmp, *ads; ++ ++ if (copy_from_user(&tmp, in, sizeof(tmp))) ++ return ERR_PTR(-EFAULT); ++ ++ if (tmp.size < sizeof(tmp)) ++ return ERR_PTR(-EINVAL); ++ ++ ads = kmalloc(tmp.size, GFP_KERNEL); ++ if (!ads) ++ return ERR_PTR(-ENOMEM); ++ ++ if (copy_from_user(ads, in, tmp.size)) { ++ kfree(ads); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ return ads; ++} ++ ++static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) ++{ ++ kfree(param); ++ return; ++} ++ ++/* ++ * Check sanity of parameter control fields and if a path is present ++ * check that it is terminated and contains at least one "/". ++ */ ++static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) ++{ ++ int err; ++ ++ if ((err = check_dev_ioctl_version(cmd, param))) { ++ AUTOFS_WARN("invalid device control module version " ++ "supplied for cmd(0x%08x)", cmd); ++ goto out; ++ } ++ ++ if (param->size > sizeof(*param)) { ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); ++ if (err) { ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ ++ err = check_name(param->path); ++ if (err) { ++ AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", ++ cmd); ++ goto out; ++ } ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* ++ * Get the autofs super block info struct from the file opened on ++ * the autofs mount point. ++ */ ++static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f) ++{ ++ struct autofs_sb_info *sbi = NULL; ++ struct inode *inode; ++ ++ if (f) { ++ inode = f->f_path.dentry->d_inode; ++ sbi = autofs4_sbi(inode->i_sb); ++ } ++ return sbi; ++} ++ ++/* Return autofs module protocol version */ ++static int autofs_dev_ioctl_protover(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protover.version = sbi->version; ++ return 0; ++} ++ ++/* Return autofs module protocol sub version */ ++static int autofs_dev_ioctl_protosubver(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->protosubver.sub_version = sbi->sub_version; ++ return 0; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested device number (aka. new_encode_dev(sb->s_dev). ++ */ ++static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ dev_t s_dev; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ inode = nd->path.dentry->d_inode; ++ if (!inode) ++ break; ++ ++ sb = inode->i_sb; ++ s_dev = new_encode_dev(sb->s_dev); ++ if (devno == s_dev) { ++ if (sb->s_magic == AUTOFS_SUPER_MAGIC) { ++ err = 0; ++ break; ++ } ++ } ++ } ++out: ++ return err; ++} ++ ++/* ++ * Walk down the mount stack looking for an autofs mount that ++ * has the requested mount type (ie. indirect, direct or offset). ++ */ ++static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) ++{ ++ struct dentry *dentry; ++ struct autofs_info *ino; ++ unsigned int err; ++ ++ err = -ENOENT; ++ ++ /* Lookup the dentry name at the base of our mount point */ ++ dentry = d_lookup(nd->path.dentry, &nd->last); ++ if (!dentry) ++ goto out; ++ ++ dput(nd->path.dentry); ++ nd->path.dentry = dentry; ++ ++ /* And follow the mount stack looking for our autofs mount */ ++ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ++ ino = autofs4_dentry_ino(nd->path.dentry); ++ if (ino && ino->sbi->type & type) { ++ err = 0; ++ break; ++ } ++ } ++out: ++ return err; ++} ++ ++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); ++ FD_SET(fd, fdt->close_on_exec); ++ 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)). ++ */ ++static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int err, fd; ++ ++ fd = get_unused_fd(); ++ if (likely(fd >= 0)) { ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ /* ++ * Search down, within the parent, looking for an ++ * autofs super block that has the device number ++ * corresponding to the autofs fs we want to open. ++ */ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) { ++ path_put(&nd.path); ++ goto out; ++ } ++ ++ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY); ++ if (IS_ERR(filp)) { ++ err = PTR_ERR(filp); ++ goto out; ++ } ++ ++ autofs_dev_ioctl_fd_install(fd, filp); ++ } ++ ++ return fd; ++ ++out: ++ put_unused_fd(fd); ++ return err; ++} ++ ++/* Open a file descriptor on an autofs mount point */ ++static int autofs_dev_ioctl_openmount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ const char *path; ++ dev_t devid; ++ int err, fd; ++ ++ /* param->path has already been checked */ ++ if (!param->openmount.devid) ++ return -EINVAL; ++ ++ param->ioctlfd = -1; ++ ++ path = param->path; ++ devid = param->openmount.devid; ++ ++ err = 0; ++ fd = autofs_dev_ioctl_open_mountpoint(path, devid); ++ if (unlikely(fd < 0)) { ++ err = fd; ++ goto out; ++ } ++ ++ param->ioctlfd = fd; ++out: ++ return err; ++} ++ ++/* Close file descriptor allocated above (user can also use close(2)). */ ++static int autofs_dev_ioctl_closemount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ return sys_close(param->ioctlfd); ++} ++ ++/* ++ * Send "ready" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_ready(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ ++ token = (autofs_wqt_t) param->ready.token; ++ return autofs4_wait_release(sbi, token, 0); ++} ++ ++/* ++ * Send "fail" status for an existing wait (either a mount or an expire ++ * request). ++ */ ++static int autofs_dev_ioctl_fail(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs_wqt_t token; ++ int status; ++ ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; ++ return autofs4_wait_release(sbi, token, status); ++} ++ ++/* ++ * Set the pipe fd for kernel communication to the daemon. ++ * ++ * Normally this is set at mount using an option but if we ++ * are reconnecting to a busy mount then we need to use this ++ * to tell the autofs mount about the new kernel pipe fd. In ++ * order to protect mounts against incorrectly setting the ++ * pipefd we also require that the autofs mount be catatonic. ++ * ++ * This also sets the process group id used to identify the ++ * controlling process (eg. the owning automount(8) daemon). ++ */ ++static int autofs_dev_ioctl_setpipefd(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ int pipefd; ++ int err = 0; ++ ++ if (param->setpipefd.pipefd == -1) ++ return -EINVAL; ++ ++ pipefd = param->setpipefd.pipefd; ++ ++ mutex_lock(&sbi->wq_mutex); ++ if (!sbi->catatonic) { ++ mutex_unlock(&sbi->wq_mutex); ++ return -EBUSY; ++ } else { ++ struct file *pipe = fget(pipefd); ++ if (!pipe->f_op || !pipe->f_op->write) { ++ err = -EPIPE; ++ fput(pipe); ++ goto out; ++ } ++ sbi->oz_pgrp = task_pgrp_nr(current); ++ sbi->pipefd = pipefd; ++ sbi->pipe = pipe; ++ sbi->catatonic = 0; ++ } ++out: ++ mutex_unlock(&sbi->wq_mutex); ++ return err; ++} ++ ++/* ++ * Make the autofs mount point catatonic, no longer responsive to ++ * mount requests. Also closes the kernel pipe file descriptor. ++ */ ++static int autofs_dev_ioctl_catatonic(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ autofs4_catatonic_mode(sbi); ++ return 0; ++} ++ ++/* Set the autofs mount timeout */ ++static int autofs_dev_ioctl_timeout(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ unsigned long timeout; ++ ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; ++ sbi->exp_timeout = timeout * HZ; ++ return 0; ++} ++ ++/* ++ * Return the uid and gid of the last request for the mount ++ * ++ * When reconstructing an autofs mount tree with active mounts ++ * we need to re-connect to mounts that may have used the original ++ * process uid and gid (or string variations of them) for mount ++ * lookups within the map entry. ++ */ ++static int autofs_dev_ioctl_requester(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct autofs_info *ino; ++ struct nameidata nd; ++ const char *path; ++ dev_t devid; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ devid = sbi->sb->s_dev; ++ ++ param->requester.uid = param->requester.gid = -1; ++ ++ /* Get nameidata of the parent directory */ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, devid); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ if (ino) { ++ err = 0; ++ autofs4_expire_wait(nd.path.dentry); ++ spin_lock(&sbi->fs_lock); ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; ++ spin_unlock(&sbi->fs_lock); ++ } ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ * more that can be done. ++ */ ++static int autofs_dev_ioctl_expire(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct vfsmount *mnt; ++ int how; ++ ++ how = param->expire.how; ++ mnt = fp->f_path.mnt; ++ ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); ++} ++ ++/* Check if autofs mount point is in use */ ++static int autofs_dev_ioctl_askumount(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ param->askumount.may_umount = 0; ++ if (may_umount(fp->f_path.mnt)) ++ param->askumount.may_umount = 1; ++ return 0; ++} ++ ++/* ++ * Check if the given path is a mountpoint. ++ * ++ * If we are supplied with the file descriptor of an autofs ++ * mount we're looking for a specific mount. In this case ++ * the path is considered a mountpoint if it is itself a ++ * mountpoint or contains a mount, such as a multi-mount ++ * without a root mount. In this case we return 1 if the ++ * path is a mount point and the super magic of the covering ++ * mount if there is one or 0 if it isn't a mountpoint. ++ * ++ * If we aren't supplied with a file descriptor then we ++ * lookup the nameidata of the path and check if it is the ++ * root of a mount. If a type is given we are looking for ++ * a particular autofs mount and if we don't find a match ++ * we return fail. If the located nameidata path is the ++ * root of a mount we return 1 along with the super magic ++ * of the mount or 0 otherwise. ++ * ++ * In both cases the the device number (as returned by ++ * new_encode_dev()) is also returned. ++ */ ++static int autofs_dev_ioctl_ismountpoint(struct file *fp, ++ struct autofs_sb_info *sbi, ++ struct autofs_dev_ioctl *param) ++{ ++ struct nameidata nd; ++ const char *path; ++ unsigned int type; ++ unsigned int devid, magic; ++ int err = -ENOENT; ++ ++ if (param->size <= sizeof(*param)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ path = param->path; ++ type = param->ismountpoint.in.type; ++ ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; ++ ++ if (!fp || param->ioctlfd == -1) { ++ if (autofs_type_any(type)) { ++ struct super_block *sb; ++ ++ err = path_lookup(path, LOOKUP_FOLLOW, &nd); ++ if (err) ++ goto out; ++ ++ sb = nd.path.dentry->d_sb; ++ devid = new_encode_dev(sb->s_dev); ++ } else { ++ struct autofs_info *ino; ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_sbi_type(&nd, type); ++ if (err) ++ goto out_release; ++ ++ ino = autofs4_dentry_ino(nd.path.dentry); ++ devid = autofs4_get_dev(ino->sbi); ++ } ++ ++ err = 0; ++ if (nd.path.dentry->d_inode && ++ nd.path.mnt->mnt_root == nd.path.dentry) { ++ err = 1; ++ magic = nd.path.dentry->d_inode->i_sb->s_magic; ++ } ++ } else { ++ dev_t dev = autofs4_get_dev(sbi); ++ ++ err = path_lookup(path, LOOKUP_PARENT, &nd); ++ if (err) ++ goto out; ++ ++ err = autofs_dev_ioctl_find_super(&nd, dev); ++ if (err) ++ goto out_release; ++ ++ devid = dev; ++ ++ err = have_submounts(nd.path.dentry); ++ ++ if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { ++ if (follow_down(&nd.path.mnt, &nd.path.dentry)) { ++ struct inode *inode = nd.path.dentry->d_inode; ++ magic = inode->i_sb->s_magic; ++ } ++ } ++ } ++ ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ ++out_release: ++ path_put(&nd.path); ++out: ++ return err; ++} ++ ++/* ++ * Our range of ioctl numbers isn't 0 based so we need to shift ++ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table ++ * lookup. ++ */ ++#define cmd_idx(cmd) (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST)) ++ ++static ioctl_fn lookup_dev_ioctl(unsigned int cmd) ++{ ++ static struct { ++ int cmd; ++ ioctl_fn fn; ++ } _ioctls[] = { ++ {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD), ++ autofs_dev_ioctl_protover}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD), ++ autofs_dev_ioctl_protosubver}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD), ++ autofs_dev_ioctl_openmount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD), ++ autofs_dev_ioctl_closemount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD), ++ autofs_dev_ioctl_ready}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD), ++ autofs_dev_ioctl_fail}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD), ++ autofs_dev_ioctl_setpipefd}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD), ++ autofs_dev_ioctl_catatonic}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD), ++ autofs_dev_ioctl_timeout}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD), ++ autofs_dev_ioctl_requester}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD), ++ autofs_dev_ioctl_expire}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD), ++ autofs_dev_ioctl_askumount}, ++ {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD), ++ autofs_dev_ioctl_ismountpoint} ++ }; ++ unsigned int idx = cmd_idx(cmd); ++ ++ return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn; ++} ++ ++/* ioctl dispatcher */ ++static int _autofs_dev_ioctl(unsigned int command, struct autofs_dev_ioctl __user *user) ++{ ++ struct autofs_dev_ioctl *param; ++ struct file *fp; ++ struct autofs_sb_info *sbi; ++ unsigned int cmd_first, cmd; ++ ioctl_fn fn = NULL; ++ int err = 0; ++ ++ /* only root can play with this */ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST); ++ cmd = _IOC_NR(command); ++ ++ if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) || ++ cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) { ++ return -ENOTTY; ++ } ++ ++ /* Copy the parameters into kernel space. */ ++ param = copy_dev_ioctl(user); ++ if (IS_ERR(param)) ++ return PTR_ERR(param); ++ ++ err = validate_dev_ioctl(command, param); ++ if (err) ++ goto out; ++ ++ /* The validate routine above always sets the version */ ++ if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD) ++ goto done; ++ ++ fn = lookup_dev_ioctl(cmd); ++ if (!fn) { ++ AUTOFS_WARN("unknown command 0x%08x", command); ++ return -ENOTTY; ++ } ++ ++ fp = NULL; ++ sbi = NULL; ++ ++ /* ++ * For obvious reasons the openmount can't have a file ++ * descriptor yet. We don't take a reference to the ++ * file during close to allow for immediate release. ++ */ ++ if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD && ++ cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) { ++ fp = fget(param->ioctlfd); ++ if (!fp) { ++ if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) ++ goto cont; ++ err = -EBADF; ++ goto out; ++ } ++ ++ if (!fp->f_op) { ++ err = -ENOTTY; ++ fput(fp); ++ goto out; ++ } ++ ++ sbi = autofs_dev_ioctl_sbi(fp); ++ if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) { ++ err = -EINVAL; ++ fput(fp); ++ goto out; ++ } ++ ++ /* ++ * Admin needs to be able to set the mount catatonic in ++ * order to be able to perform the re-open. ++ */ ++ if (!autofs4_oz_mode(sbi) && ++ cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) { ++ err = -EACCES; ++ fput(fp); ++ goto out; ++ } ++ } ++cont: ++ err = fn(fp, sbi, param); ++ ++ if (fp) ++ fput(fp); ++done: ++ if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE)) ++ err = -EFAULT; ++out: ++ free_dev_ioctl(param); ++ return err; ++} ++ ++static long autofs_dev_ioctl(struct file *file, uint command, ulong u) ++{ ++ int err; ++ err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u); ++ return (long) err; ++} ++ ++#ifdef CONFIG_COMPAT ++static long autofs_dev_ioctl_compat(struct file *file, uint command, ulong u) ++{ ++ return (long) autofs_dev_ioctl(file, command, (ulong) compat_ptr(u)); ++} ++#else ++#define autofs_dev_ioctl_compat NULL ++#endif ++ ++static const struct file_operations _dev_ioctl_fops = { ++ .unlocked_ioctl = autofs_dev_ioctl, ++ .compat_ioctl = autofs_dev_ioctl_compat, ++ .owner = THIS_MODULE, ++}; ++ ++static struct miscdevice _autofs_dev_ioctl_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = AUTOFS_DEVICE_NAME, ++ .fops = &_dev_ioctl_fops ++}; ++ ++/* Register/deregister misc character device */ ++int autofs_dev_ioctl_init(void) ++{ ++ int r; ++ ++ r = misc_register(&_autofs_dev_ioctl_misc); ++ if (r) { ++ AUTOFS_ERROR("misc_register failed for control device"); ++ return r; ++ } ++ ++ return 0; ++} ++ ++void autofs_dev_ioctl_exit(void) ++{ ++ misc_deregister(&_autofs_dev_ioctl_misc); ++ return; ++} ++ +--- linux-2.6.27.orig/fs/autofs4/init.c ++++ linux-2.6.27/fs/autofs4/init.c +@@ -29,11 +29,20 @@ static struct file_system_type autofs_fs + + static int __init init_autofs4_fs(void) + { +- return register_filesystem(&autofs_fs_type); ++ int err; ++ ++ err = register_filesystem(&autofs_fs_type); ++ if (err) ++ return err; ++ ++ autofs_dev_ioctl_init(); ++ ++ return err; + } + + static void __exit exit_autofs4_fs(void) + { ++ autofs_dev_ioctl_exit(); + unregister_filesystem(&autofs_fs_type); + } + +--- /dev/null ++++ linux-2.6.27/include/linux/auto_dev-ioctl.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2008 Red Hat, Inc. All rights reserved. ++ * Copyright 2008 Ian Kent ++ * ++ * This file is part of the Linux kernel and is made available under ++ * the terms of the GNU General Public License, version 2, or at your ++ * option, any later version, incorporated herein by reference. ++ */ ++ ++#ifndef _LINUX_AUTO_DEV_IOCTL_H ++#define _LINUX_AUTO_DEV_IOCTL_H ++ ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ ++ ++#define AUTOFS_DEVICE_NAME "autofs" ++ ++#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 ++#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 ++ ++#define AUTOFS_DEVID_LEN 16 ++ ++#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) ++ ++/* ++ * An ioctl interface for autofs mount point control. ++ */ ++ ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ ++/* ++ * All the ioctls use this structure. ++ * When sending a path size must account for the total length ++ * of the chunk of memory otherwise is is the size of the ++ * structure. ++ */ ++ ++struct autofs_dev_ioctl { ++ __u32 ver_major; ++ __u32 ver_minor; ++ __u32 size; /* total size of data passed in ++ * including this struct */ ++ __s32 ioctlfd; /* automount command fd */ ++ ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; ++ ++ char path[0]; ++}; ++ ++static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) ++{ ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); ++ in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; ++ in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; ++ in->size = sizeof(struct autofs_dev_ioctl); ++ in->ioctlfd = -1; ++ return; ++} ++ ++/* ++ * If you change this make sure you make the corresponding change ++ * to autofs-dev-ioctl.c:lookup_ioctl() ++ */ ++enum { ++ /* Get various version info */ ++ AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, ++ ++ /* Open mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, ++ ++ /* Close mount ioctl fd */ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, ++ ++ /* Mount/expire status returns */ ++ AUTOFS_DEV_IOCTL_READY_CMD, ++ AUTOFS_DEV_IOCTL_FAIL_CMD, ++ ++ /* Activate/deactivate autofs mount */ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, ++ ++ /* Expiry timeout */ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, ++ ++ /* Get mount last requesting uid and gid */ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, ++ ++ /* Check for eligible expire candidates */ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, ++ ++ /* Request busy status */ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, ++ ++ /* Check if path is a mountpoint */ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, ++}; ++ ++#define AUTOFS_IOCTL 0x93 ++ ++#define AUTOFS_DEV_IOCTL_VERSION \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_OPENMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_READY \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_FAIL \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_SETPIPEFD \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_CATATONIC \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_TIMEOUT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_REQUESTER \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_EXPIRE \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) ++ ++#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ ++ _IOWR(AUTOFS_IOCTL, \ ++ AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) ++ ++#endif /* _LINUX_AUTO_DEV_IOCTL_H */ +--- linux-2.6.27.orig/fs/autofs4/root.c ++++ linux-2.6.27/fs/autofs4/root.c +@@ -485,22 +485,6 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); +- if (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, so just wait for it. +- */ +- ino = autofs4_dentry_ino(expiring); +- autofs4_expire_wait(expiring); +- spin_lock(&sbi->lookup_lock); +- if (!list_empty(&ino->expiring)) +- list_del_init(&ino->expiring); +- spin_unlock(&sbi->lookup_lock); +- dput(expiring); +- } +- + unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); + if (unhashed) + dentry = unhashed; +@@ -538,14 +522,31 @@ static struct dentry *autofs4_lookup(str + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); ++ if (dentry->d_op && dentry->d_op->d_revalidate) + (dentry->d_op->d_revalidate)(dentry, nd); +- mutex_lock(&dir->i_mutex); +- } ++ mutex_lock(&dir->i_mutex); + } + + /* +--- linux-2.6.27.orig/include/linux/auto_fs.h ++++ linux-2.6.27/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.28-v5-update-20090903.patch b/patches/autofs4-2.6.28-v5-update-20090903.patch new file mode 100644 index 0000000..84757cb --- /dev/null +++ b/patches/autofs4-2.6.28-v5-update-20090903.patch @@ -0,0 +1,908 @@ +--- linux-2.6.28.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.28/fs/autofs4/autofs_i.h +@@ -25,8 +25,6 @@ + #define AUTOFS_DEV_IOCTL_IOC_FIRST (AUTOFS_DEV_IOCTL_VERSION) + #define AUTOFS_DEV_IOCTL_IOC_COUNT (AUTOFS_IOC_COUNT - 11) + +-#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) +- + #include + #include + #include +@@ -188,6 +186,8 @@ int autofs4_expire_wait(struct dentry *d + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); + struct dentry *autofs4_expire_direct(struct super_block *sb, +--- linux-2.6.28.orig/fs/autofs4/dev-ioctl.c ++++ linux-2.6.28/fs/autofs4/dev-ioctl.c +@@ -124,7 +124,7 @@ static inline void free_dev_ioctl(struct + + /* + * Check sanity of parameter control fields and if a path is present +- * check that it has a "/" and is terminated. ++ * check that it is terminated and contains at least one "/". + */ + static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) + { +@@ -138,15 +138,16 @@ static int validate_dev_ioctl(int cmd, s + } + + if (param->size > sizeof(*param)) { +- err = check_name(param->path); ++ err = invalid_str(param->path, ++ (void *) ((size_t) param + param->size)); + if (err) { +- AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", +- cmd); ++ AUTOFS_WARN( ++ "path string terminator missing for cmd(0x%08x)", ++ cmd); + goto out; + } + +- err = invalid_str(param->path, +- (void *) ((size_t) param + param->size)); ++ err = check_name(param->path); + if (err) { + AUTOFS_WARN("invalid path supplied for cmd(0x%08x)", + cmd); +@@ -180,7 +181,7 @@ static int autofs_dev_ioctl_protover(str + struct autofs_sb_info *sbi, + struct autofs_dev_ioctl *param) + { +- param->arg1 = sbi->version; ++ param->protover.version = sbi->version; + return 0; + } + +@@ -189,7 +190,7 @@ static int autofs_dev_ioctl_protosubver( + struct autofs_sb_info *sbi, + struct autofs_dev_ioctl *param) + { +- param->arg1 = sbi->sub_version; ++ param->protosubver.sub_version = sbi->sub_version; + return 0; + } + +@@ -334,13 +335,13 @@ static int autofs_dev_ioctl_openmount(st + int err, fd; + + /* param->path has already been checked */ +- if (!param->arg1) ++ if (!param->openmount.devid) + return -EINVAL; + + param->ioctlfd = -1; + + path = param->path; +- devid = param->arg1; ++ devid = param->openmount.devid; + + err = 0; + fd = autofs_dev_ioctl_open_mountpoint(path, devid); +@@ -372,7 +373,7 @@ static int autofs_dev_ioctl_ready(struct + { + autofs_wqt_t token; + +- token = (autofs_wqt_t) param->arg1; ++ token = (autofs_wqt_t) param->ready.token; + return autofs4_wait_release(sbi, token, 0); + } + +@@ -387,8 +388,8 @@ static int autofs_dev_ioctl_fail(struct + autofs_wqt_t token; + int status; + +- token = (autofs_wqt_t) param->arg1; +- status = param->arg2 ? param->arg2 : -ENOENT; ++ token = (autofs_wqt_t) param->fail.token; ++ status = param->fail.status ? param->fail.status : -ENOENT; + return autofs4_wait_release(sbi, token, status); + } + +@@ -411,10 +412,10 @@ static int autofs_dev_ioctl_setpipefd(st + int pipefd; + int err = 0; + +- if (param->arg1 == -1) ++ if (param->setpipefd.pipefd == -1) + return -EINVAL; + +- pipefd = param->arg1; ++ pipefd = param->setpipefd.pipefd; + + mutex_lock(&sbi->wq_mutex); + if (!sbi->catatonic) { +@@ -456,8 +457,8 @@ static int autofs_dev_ioctl_timeout(stru + { + unsigned long timeout; + +- timeout = param->arg1; +- param->arg1 = sbi->exp_timeout / HZ; ++ timeout = param->timeout.timeout; ++ param->timeout.timeout = sbi->exp_timeout / HZ; + sbi->exp_timeout = timeout * HZ; + return 0; + } +@@ -488,7 +489,7 @@ static int autofs_dev_ioctl_requester(st + path = param->path; + devid = sbi->sb->s_dev; + +- param->arg1 = param->arg2 = -1; ++ param->requester.uid = param->requester.gid = -1; + + /* Get nameidata of the parent directory */ + err = path_lookup(path, LOOKUP_PARENT, &nd); +@@ -504,8 +505,8 @@ static int autofs_dev_ioctl_requester(st + err = 0; + autofs4_expire_wait(nd.path.dentry); + spin_lock(&sbi->fs_lock); +- param->arg1 = ino->uid; +- param->arg2 = ino->gid; ++ param->requester.uid = ino->uid; ++ param->requester.gid = ino->gid; + spin_unlock(&sbi->fs_lock); + } + +@@ -523,40 +524,13 @@ static int autofs_dev_ioctl_expire(struc + struct autofs_sb_info *sbi, + struct autofs_dev_ioctl *param) + { +- struct dentry *dentry; + struct vfsmount *mnt; +- int err = -EAGAIN; + int how; + +- how = param->arg1; ++ how = param->expire.how; + mnt = fp->f_path.mnt; + +- if (sbi->type & AUTOFS_TYPE_TRIGGER) +- dentry = autofs4_expire_direct(sbi->sb, mnt, sbi, how); +- else +- dentry = autofs4_expire_indirect(sbi->sb, mnt, sbi, how); +- +- if (dentry) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- +- /* +- * This is synchronous because it makes the daemon a +- * little easier +- */ +- err = autofs4_wait(sbi, dentry, NFY_EXPIRE); +- +- spin_lock(&sbi->fs_lock); +- if (ino->flags & AUTOFS_INF_MOUNTPOINT) { +- ino->flags &= ~AUTOFS_INF_MOUNTPOINT; +- sbi->sb->s_root->d_mounted++; +- } +- ino->flags &= ~AUTOFS_INF_EXPIRING; +- complete_all(&ino->expire_complete); +- spin_unlock(&sbi->fs_lock); +- dput(dentry); +- } +- +- return err; ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); + } + + /* Check if autofs mount point is in use */ +@@ -564,9 +538,9 @@ static int autofs_dev_ioctl_askumount(st + struct autofs_sb_info *sbi, + struct autofs_dev_ioctl *param) + { +- param->arg1 = 0; ++ param->askumount.may_umount = 0; + if (may_umount(fp->f_path.mnt)) +- param->arg1 = 1; ++ param->askumount.may_umount = 1; + return 0; + } + +@@ -599,6 +573,7 @@ static int autofs_dev_ioctl_ismountpoint + struct nameidata nd; + const char *path; + unsigned int type; ++ unsigned int devid, magic; + int err = -ENOENT; + + if (param->size <= sizeof(*param)) { +@@ -607,13 +582,13 @@ static int autofs_dev_ioctl_ismountpoint + } + + path = param->path; +- type = param->arg1; ++ type = param->ismountpoint.in.type; + +- param->arg1 = 0; +- param->arg2 = 0; ++ param->ismountpoint.out.devid = devid = 0; ++ param->ismountpoint.out.magic = magic = 0; + + if (!fp || param->ioctlfd == -1) { +- if (type == AUTOFS_TYPE_ANY) { ++ if (autofs_type_any(type)) { + struct super_block *sb; + + err = path_lookup(path, LOOKUP_FOLLOW, &nd); +@@ -621,7 +596,7 @@ static int autofs_dev_ioctl_ismountpoint + goto out; + + sb = nd.path.dentry->d_sb; +- param->arg1 = new_encode_dev(sb->s_dev); ++ devid = new_encode_dev(sb->s_dev); + } else { + struct autofs_info *ino; + +@@ -634,38 +609,41 @@ static int autofs_dev_ioctl_ismountpoint + goto out_release; + + ino = autofs4_dentry_ino(nd.path.dentry); +- param->arg1 = autofs4_get_dev(ino->sbi); ++ devid = autofs4_get_dev(ino->sbi); + } + + err = 0; + if (nd.path.dentry->d_inode && + nd.path.mnt->mnt_root == nd.path.dentry) { + err = 1; +- param->arg2 = nd.path.dentry->d_inode->i_sb->s_magic; ++ magic = nd.path.dentry->d_inode->i_sb->s_magic; + } + } else { +- dev_t devid = new_encode_dev(sbi->sb->s_dev); ++ dev_t dev = autofs4_get_dev(sbi); + + err = path_lookup(path, LOOKUP_PARENT, &nd); + if (err) + goto out; + +- err = autofs_dev_ioctl_find_super(&nd, devid); ++ err = autofs_dev_ioctl_find_super(&nd, dev); + if (err) + goto out_release; + +- param->arg1 = autofs4_get_dev(sbi); ++ devid = dev; + + err = have_submounts(nd.path.dentry); + + if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { + if (follow_down(&nd.path.mnt, &nd.path.dentry)) { + struct inode *inode = nd.path.dentry->d_inode; +- param->arg2 = inode->i_sb->s_magic; ++ magic = inode->i_sb->s_magic; + } + } + } + ++ param->ismountpoint.out.devid = devid; ++ param->ismountpoint.out.magic = magic; ++ + out_release: + path_put(&nd.path); + out: +--- linux-2.6.28.orig/fs/autofs4/expire.c ++++ linux-2.6.28/fs/autofs4/expire.c +@@ -63,15 +63,17 @@ static int autofs4_mount_busy(struct vfs + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + + /* This is an autofs submount, we can't expire it */ +- if (sbi->type == AUTOFS_TYPE_INDIRECT) ++ if (autofs_type_indirect(sbi->type)) + goto done; + + /* + * Otherwise it's an offset mount and we need to check + * if we can umount its mount, if there is one. + */ +- if (!d_mountpoint(dentry)) ++ if (!d_mountpoint(dentry)) { ++ status = 0; + goto done; ++ } + } + + /* Update the expiry counter if fs is busy */ +@@ -478,22 +480,16 @@ int autofs4_expire_run(struct super_bloc + return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; + +- if (arg && get_user(do_now, arg)) +- return -EFAULT; +- +- if (sbi->type & AUTOFS_TYPE_TRIGGER) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ if (autofs_type_trigger(sbi->type)) ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); +@@ -516,3 +512,16 @@ int autofs4_expire_multi(struct super_bl + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.28.orig/fs/autofs4/inode.c ++++ linux-2.6.28/fs/autofs4/inode.c +@@ -197,9 +197,9 @@ static int autofs4_show_options(struct s + seq_printf(m, ",minproto=%d", sbi->min_proto); + seq_printf(m, ",maxproto=%d", sbi->max_proto); + +- if (sbi->type & AUTOFS_TYPE_OFFSET) ++ if (autofs_type_offset(sbi->type)) + seq_printf(m, ",offset"); +- else if (sbi->type & AUTOFS_TYPE_DIRECT) ++ else if (autofs_type_direct(sbi->type)) + seq_printf(m, ",direct"); + else + seq_printf(m, ",indirect"); +@@ -284,13 +284,13 @@ static int parse_options(char *options, + *maxproto = option; + break; + case Opt_indirect: +- *type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(type); + break; + case Opt_direct: +- *type = AUTOFS_TYPE_DIRECT; ++ set_autofs_type_direct(type); + break; + case Opt_offset: +- *type = AUTOFS_TYPE_OFFSET; ++ set_autofs_type_offset(type); + break; + default: + return 1; +@@ -338,7 +338,7 @@ int autofs4_fill_super(struct super_bloc + sbi->sb = s; + sbi->version = 0; + sbi->sub_version = 0; +- sbi->type = AUTOFS_TYPE_INDIRECT; ++ set_autofs_type_indirect(&sbi->type); + sbi->min_proto = 0; + sbi->max_proto = 0; + mutex_init(&sbi->wq_mutex); +@@ -380,7 +380,7 @@ int autofs4_fill_super(struct super_bloc + } + + root_inode->i_fop = &autofs4_root_operations; +- root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? ++ root_inode->i_op = autofs_type_trigger(sbi->type) ? + &autofs4_direct_root_inode_operations : + &autofs4_indirect_root_inode_operations; + +--- linux-2.6.28.orig/fs/autofs4/waitq.c ++++ linux-2.6.28/fs/autofs4/waitq.c +@@ -297,20 +297,14 @@ static int validate_request(struct autof + */ + if (notify == NFY_MOUNT) { + /* +- * If the dentry isn't hashed just go ahead and try the +- * mount again with a new wait (not much else we can do). +- */ +- if (!d_unhashed(dentry)) { +- /* +- * But if the dentry is hashed, that means that we +- * got here through the revalidate path. Thus, we +- * need to check if the dentry has been mounted +- * while we waited on the wq_mutex. If it has, +- * simply return success. +- */ +- if (d_mountpoint(dentry)) +- return 0; +- } ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) ++ return 0; + } + + return 1; +@@ -337,7 +331,7 @@ int autofs4_wait(struct autofs_sb_info * + * is very similar for indirect mounts except only dentrys + * in the root of the autofs file system may be negative. + */ +- if (sbi->type & AUTOFS_TYPE_TRIGGER) ++ if (autofs_type_trigger(sbi->type)) + return -ENOENT; + else if (!IS_ROOT(dentry->d_parent)) + return -ENOENT; +@@ -348,7 +342,7 @@ int autofs4_wait(struct autofs_sb_info * + return -ENOMEM; + + /* If this is a direct mount request create a dummy name */ +- if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) ++ if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type)) + qstr.len = sprintf(name, "%p", dentry); + else { + qstr.len = autofs4_getpath(sbi, dentry, &name); +@@ -406,11 +400,11 @@ int autofs4_wait(struct autofs_sb_info * + type = autofs_ptype_expire_multi; + } else { + if (notify == NFY_MOUNT) +- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_missing_direct : + autofs_ptype_missing_indirect; + else +- type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? ++ type = autofs_type_trigger(sbi->type) ? + autofs_ptype_expire_direct : + autofs_ptype_expire_indirect; + } +--- linux-2.6.28.orig/include/linux/auto_fs4.h ++++ linux-2.6.28/include/linux/auto_fs4.h +@@ -29,10 +29,64 @@ + #define AUTOFS_EXP_IMMEDIATE 1 + #define AUTOFS_EXP_LEAVES 2 + +-#define AUTOFS_TYPE_ANY 0x0000 +-#define AUTOFS_TYPE_INDIRECT 0x0001 +-#define AUTOFS_TYPE_DIRECT 0x0002 +-#define AUTOFS_TYPE_OFFSET 0x0004 ++#define AUTOFS_TYPE_ANY 0U ++#define AUTOFS_TYPE_INDIRECT 1U ++#define AUTOFS_TYPE_DIRECT 2U ++#define AUTOFS_TYPE_OFFSET 4U ++ ++static inline void set_autofs_type_indirect(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_INDIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_indirect(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_INDIRECT); ++} ++ ++static inline void set_autofs_type_direct(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_DIRECT; ++ return; ++} ++ ++static inline unsigned int autofs_type_direct(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT); ++} ++ ++static inline void set_autofs_type_offset(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_OFFSET; ++ return; ++} ++ ++static inline unsigned int autofs_type_offset(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_OFFSET); ++} ++ ++static inline unsigned int autofs_type_trigger(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_DIRECT || type == AUTOFS_TYPE_OFFSET); ++} ++ ++/* ++ * This isn't really a type as we use it to say "no type set" to ++ * indicate we want to search for "any" mount in the ++ * autofs_dev_ioctl_ismountpoint() device ioctl function. ++ */ ++static inline void set_autofs_type_any(unsigned int *type) ++{ ++ *type = AUTOFS_TYPE_ANY; ++ return; ++} ++ ++static inline unsigned int autofs_type_any(unsigned int type) ++{ ++ return (type == AUTOFS_TYPE_ANY); ++} + + /* Daemon notification packet types */ + enum autofs_notify { +--- linux-2.6.28.orig/Documentation/filesystems/autofs4-mount-control.txt ++++ linux-2.6.28/Documentation/filesystems/autofs4-mount-control.txt +@@ -179,8 +179,21 @@ struct autofs_dev_ioctl { + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + +- __u32 arg1; /* Command parameters */ +- __u32 arg2; ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; + + char path[0]; + }; +@@ -192,8 +205,8 @@ optionally be used to check a specific m + mount point file descriptor, and when requesting the uid and gid of the + last successful mount on a directory within the autofs file system. + +-The fields arg1 and arg2 are used to communicate parameters and results of +-calls made as described below. ++The anonymous union is used to communicate parameters and results of calls ++made as described below. + + The path field is used to pass a path where it is needed and the size field + is used account for the increased structure length when translating the +@@ -245,25 +258,27 @@ AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS + Get the major and minor version of the autofs4 protocol version understood + by loaded module. This call requires an initialized struct autofs_dev_ioctl + with the ioctlfd field set to a valid autofs mount point descriptor +-and sets the requested version number in structure field arg1. These +-commands return 0 on success or one of the negative error codes if +-validation fails. ++and sets the requested version number in structure field protover.version ++and ptotosubver.sub_version respectively. These commands return 0 on ++success or one of the negative error codes if validation fails. + + +-AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT +----------------------------------------------------------- ++AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD ++------------------------------------------------------------------ + + Obtain and release a file descriptor for an autofs managed mount point + path. The open call requires an initialized struct autofs_dev_ioctl with + the the path field set and the size field adjusted appropriately as well +-as the arg1 field set to the device number of the autofs mount. The +-device number can be obtained from the mount options shown in +-/proc/mounts. The close call requires an initialized struct +-autofs_dev_ioct with the ioctlfd field set to the descriptor obtained +-from the open call. The release of the file descriptor can also be done +-with close(2) so any open descriptors will also be closed at process exit. +-The close call is included in the implemented operations largely for +-completeness and to provide for a consistent user space implementation. ++as the openmount.devid field set to the device number of the autofs mount. ++The device number of an autofs mounted filesystem can be obtained by using ++the AUTOFS_DEV_IOCTL_ISMOUNTPOINT ioctl function by providing the path ++and autofs mount type, as described below. The close call requires an ++initialized struct autofs_dev_ioct with the ioctlfd field set to the ++descriptor obtained from the open call. The release of the file descriptor ++can also be done with close(2) so any open descriptors will also be ++closed at process exit. The close call is included in the implemented ++operations largely for completeness and to provide for a consistent ++user space implementation. + + + AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD +@@ -272,10 +287,10 @@ AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DE + Return mount and expire result status from user space to the kernel. + Both of these calls require an initialized struct autofs_dev_ioctl + with the ioctlfd field set to the descriptor obtained from the open +-call and the arg1 field set to the wait queue token number, received +-by user space in the foregoing mount or expire request. The arg2 field +-is set to the status to be returned. For the ready call this is always +-0 and for the fail call it is set to the errno of the operation. ++call and the ready.token or fail.token field set to the wait queue ++token number, received by user space in the foregoing mount or expire ++request. The fail.status field is set to the status to be returned when ++sending a failure notification with AUTOFS_DEV_IOCTL_FAIL_CMD. + + + AUTOFS_DEV_IOCTL_SETPIPEFD_CMD +@@ -290,9 +305,10 @@ mount be catatonic (see next call). + + The call requires an initialized struct autofs_dev_ioctl with the + ioctlfd field set to the descriptor obtained from the open call and +-the arg1 field set to descriptor of the pipe. On success the call +-also sets the process group id used to identify the controlling process +-(eg. the owning automount(8) daemon) to the process group of the caller. ++the setpipefd.pipefd field set to descriptor of the pipe. On success ++the call also sets the process group id used to identify the controlling ++process (eg. the owning automount(8) daemon) to the process group of ++the caller. + + + AUTOFS_DEV_IOCTL_CATATONIC_CMD +@@ -313,6 +329,9 @@ Set the expire timeout for mounts within + + The call requires an initialized struct autofs_dev_ioctl with the + ioctlfd field set to the descriptor obtained from the open call. ++The timeout.timeout field is set to the desired timeout and this ++field is set to the value of the value of the current timeout of ++the mount upon successful completion. + + + AUTOFS_DEV_IOCTL_REQUESTER_CMD +@@ -323,9 +342,9 @@ mount on the given path dentry. + + The call requires an initialized struct autofs_dev_ioctl with the path + field set to the mount point in question and the size field adjusted +-appropriately as well as the arg1 field set to the device number of the +-containing autofs mount. Upon return the struct field arg1 contains the +-uid and arg2 the gid. ++appropriately as well as the ioctlfd field set to the descriptor obtained ++from the open call. Upon return the struct fields requester.uid and ++requester.gid contain the uid and gid respectively. + + When reconstructing an autofs mount tree with active mounts we need to + re-connect to mounts that may have used the original process uid and +@@ -343,8 +362,8 @@ this ioctl is called until no further ex + The call requires an initialized struct autofs_dev_ioctl with the + ioctlfd field set to the descriptor obtained from the open call. In + addition an immediate expire, independent of the mount timeout, can be +-requested by setting the arg1 field to 1. If no expire candidates can +-be found the ioctl returns -1 with errno set to EAGAIN. ++requested by setting the expire.how field to 1. If no expire candidates ++can be found the ioctl returns -1 with errno set to EAGAIN. + + This call causes the kernel module to check the mount corresponding + to the given ioctlfd for mounts that can be expired, issues an expire +@@ -357,7 +376,8 @@ Checks if an autofs mount point is in us + + The call requires an initialized struct autofs_dev_ioctl with the + ioctlfd field set to the descriptor obtained from the open call and +-it returns the result in the arg1 field, 1 for busy and 0 otherwise. ++it returns the result in the askumount.may_umount field, 1 for busy ++and 0 otherwise. + + + AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD +@@ -367,14 +387,15 @@ Check if the given path is a mountpoint. + + The call requires an initialized struct autofs_dev_ioctl. There are two + possible variations. Both use the path field set to the path of the mount +-point to check and the size field adjusted appropriately. One uses the +-ioctlfd field to identify a specific mount point to check while the other +-variation uses the path and optionaly arg1 set to an autofs mount type. +-The call returns 1 if this is a mount point and sets arg1 to the device +-number of the mount and field arg2 to the relevant super block magic +-number (described below) or 0 if it isn't a mountpoint. In both cases +-the the device number (as returned by new_encode_dev()) is returned +-in field arg1. ++point to check and the size field must be adjusted appropriately. One uses ++the ioctlfd field to identify a specific mount point to check while the ++other variation uses the path and optionaly the ismountpoint.in.type ++field set to an autofs mount type. The call returns 1 if this is a mount ++point and sets the ismountpoint.out.devid field to the device number of ++the mount and the ismountpoint.out.magic field to the relevant super ++block magic number (described below) or 0 if it isn't a mountpoint. In ++both cases the the device number (as returned by new_encode_dev()) is ++returned in the ismountpoint.out.devid field. + + If supplied with a file descriptor we're looking for a specific mount, + not necessarily at the top of the mounted stack. In this case the path +--- linux-2.6.28.orig/include/linux/auto_dev-ioctl.h ++++ linux-2.6.28/include/linux/auto_dev-ioctl.h +@@ -10,7 +10,13 @@ + #ifndef _LINUX_AUTO_DEV_IOCTL_H + #define _LINUX_AUTO_DEV_IOCTL_H + +-#include ++#include ++ ++#ifdef __KERNEL__ ++#include ++#else ++#include ++#endif /* __KERNEL__ */ + + #define AUTOFS_DEVICE_NAME "autofs" + +@@ -25,6 +31,60 @@ + * An ioctl interface for autofs mount point control. + */ + ++struct args_protover { ++ __u32 version; ++}; ++ ++struct args_protosubver { ++ __u32 sub_version; ++}; ++ ++struct args_openmount { ++ __u32 devid; ++}; ++ ++struct args_ready { ++ __u32 token; ++}; ++ ++struct args_fail { ++ __u32 token; ++ __s32 status; ++}; ++ ++struct args_setpipefd { ++ __s32 pipefd; ++}; ++ ++struct args_timeout { ++ __u64 timeout; ++}; ++ ++struct args_requester { ++ __u32 uid; ++ __u32 gid; ++}; ++ ++struct args_expire { ++ __u32 how; ++}; ++ ++struct args_askumount { ++ __u32 may_umount; ++}; ++ ++struct args_ismountpoint { ++ union { ++ struct args_in { ++ __u32 type; ++ } in; ++ struct args_out { ++ __u32 devid; ++ __u32 magic; ++ } out; ++ }; ++}; ++ + /* + * All the ioctls use this structure. + * When sending a path size must account for the total length +@@ -39,20 +99,32 @@ struct autofs_dev_ioctl { + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + +- __u32 arg1; /* Command parameters */ +- __u32 arg2; ++ /* Command parameters */ ++ ++ union { ++ struct args_protover protover; ++ struct args_protosubver protosubver; ++ struct args_openmount openmount; ++ struct args_ready ready; ++ struct args_fail fail; ++ struct args_setpipefd setpipefd; ++ struct args_timeout timeout; ++ struct args_requester requester; ++ struct args_expire expire; ++ struct args_askumount askumount; ++ struct args_ismountpoint ismountpoint; ++ }; + + char path[0]; + }; + + static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) + { ++ memset(in, 0, sizeof(struct autofs_dev_ioctl)); + in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; + in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; + in->size = sizeof(struct autofs_dev_ioctl); + in->ioctlfd = -1; +- in->arg1 = 0; +- in->arg2 = 0; + return; + } + +--- linux-2.6.28.orig/fs/autofs4/root.c ++++ linux-2.6.28/fs/autofs4/root.c +@@ -485,22 +485,6 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); +- if (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, so just wait for it. +- */ +- ino = autofs4_dentry_ino(expiring); +- autofs4_expire_wait(expiring); +- spin_lock(&sbi->lookup_lock); +- if (!list_empty(&ino->expiring)) +- list_del_init(&ino->expiring); +- spin_unlock(&sbi->lookup_lock); +- dput(expiring); +- } +- + unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); + if (unhashed) + dentry = unhashed; +@@ -538,14 +522,31 @@ static struct dentry *autofs4_lookup(str + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); ++ if (dentry->d_op && dentry->d_op->d_revalidate) + (dentry->d_op->d_revalidate)(dentry, nd); +- mutex_lock(&dir->i_mutex); +- } ++ mutex_lock(&dir->i_mutex); + } + + /* +--- linux-2.6.28.orig/include/linux/auto_fs.h ++++ linux-2.6.28/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + diff --git a/patches/autofs4-2.6.29-v5-update-20090903.patch b/patches/autofs4-2.6.29-v5-update-20090903.patch new file mode 100644 index 0000000..9701be5 --- /dev/null +++ b/patches/autofs4-2.6.29-v5-update-20090903.patch @@ -0,0 +1,240 @@ +--- linux-2.6.29.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.29/fs/autofs4/autofs_i.h +@@ -186,6 +186,8 @@ int autofs4_expire_wait(struct dentry *d + int autofs4_expire_run(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, + struct autofs_packet_expire __user *); ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when); + int autofs4_expire_multi(struct super_block *, struct vfsmount *, + struct autofs_sb_info *, int __user *); + struct dentry *autofs4_expire_direct(struct super_block *sb, +--- linux-2.6.29.orig/fs/autofs4/dev-ioctl.c ++++ linux-2.6.29/fs/autofs4/dev-ioctl.c +@@ -525,40 +525,13 @@ static int autofs_dev_ioctl_expire(struc + struct autofs_sb_info *sbi, + struct autofs_dev_ioctl *param) + { +- struct dentry *dentry; + struct vfsmount *mnt; +- int err = -EAGAIN; + int how; + + how = param->expire.how; + mnt = fp->f_path.mnt; + +- if (autofs_type_trigger(sbi->type)) +- dentry = autofs4_expire_direct(sbi->sb, mnt, sbi, how); +- else +- dentry = autofs4_expire_indirect(sbi->sb, mnt, sbi, how); +- +- if (dentry) { +- struct autofs_info *ino = autofs4_dentry_ino(dentry); +- +- /* +- * This is synchronous because it makes the daemon a +- * little easier +- */ +- err = autofs4_wait(sbi, dentry, NFY_EXPIRE); +- +- spin_lock(&sbi->fs_lock); +- if (ino->flags & AUTOFS_INF_MOUNTPOINT) { +- ino->flags &= ~AUTOFS_INF_MOUNTPOINT; +- sbi->sb->s_root->d_mounted++; +- } +- ino->flags &= ~AUTOFS_INF_EXPIRING; +- complete_all(&ino->expire_complete); +- spin_unlock(&sbi->fs_lock); +- dput(dentry); +- } +- +- return err; ++ return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how); + } + + /* Check if autofs mount point is in use */ +--- linux-2.6.29.orig/fs/autofs4/expire.c ++++ linux-2.6.29/fs/autofs4/expire.c +@@ -70,8 +70,10 @@ static int autofs4_mount_busy(struct vfs + * Otherwise it's an offset mount and we need to check + * if we can umount its mount, if there is one. + */ +- if (!d_mountpoint(dentry)) ++ if (!d_mountpoint(dentry)) { ++ status = 0; + goto done; ++ } + } + + /* Update the expiry counter if fs is busy */ +@@ -478,22 +480,16 @@ int autofs4_expire_run(struct super_bloc + return ret; + } + +-/* Call repeatedly until it returns -EAGAIN, meaning there's nothing +- more to be done */ +-int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, +- struct autofs_sb_info *sbi, int __user *arg) ++int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int when) + { + struct dentry *dentry; + int ret = -EAGAIN; +- int do_now = 0; +- +- if (arg && get_user(do_now, arg)) +- return -EFAULT; + + if (autofs_type_trigger(sbi->type)) +- dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_direct(sb, mnt, sbi, when); + else +- dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); ++ dentry = autofs4_expire_indirect(sb, mnt, sbi, when); + + if (dentry) { + struct autofs_info *ino = autofs4_dentry_ino(dentry); +@@ -516,3 +512,16 @@ int autofs4_expire_multi(struct super_bl + return ret; + } + ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, ++ struct autofs_sb_info *sbi, int __user *arg) ++{ ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ return autofs4_do_expire_multi(sb, mnt, sbi, do_now); ++} ++ +--- linux-2.6.29.orig/fs/autofs4/root.c ++++ linux-2.6.29/fs/autofs4/root.c +@@ -485,22 +485,6 @@ static struct dentry *autofs4_lookup(str + DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", + current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); + +- expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); +- if (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, so just wait for it. +- */ +- ino = autofs4_dentry_ino(expiring); +- autofs4_expire_wait(expiring); +- spin_lock(&sbi->lookup_lock); +- if (!list_empty(&ino->expiring)) +- list_del_init(&ino->expiring); +- spin_unlock(&sbi->lookup_lock); +- dput(expiring); +- } +- + unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); + if (unhashed) + dentry = unhashed; +@@ -538,14 +522,31 @@ static struct dentry *autofs4_lookup(str + } + + if (!oz_mode) { ++ mutex_unlock(&dir->i_mutex); ++ expiring = autofs4_lookup_expiring(sbi, ++ dentry->d_parent, ++ &dentry->d_name); ++ if (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, so just wait for it. ++ */ ++ ino = autofs4_dentry_ino(expiring); ++ autofs4_expire_wait(expiring); ++ spin_lock(&sbi->lookup_lock); ++ if (!list_empty(&ino->expiring)) ++ list_del_init(&ino->expiring); ++ spin_unlock(&sbi->lookup_lock); ++ dput(expiring); ++ } ++ + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + spin_unlock(&dentry->d_lock); +- if (dentry->d_op && dentry->d_op->d_revalidate) { +- mutex_unlock(&dir->i_mutex); ++ if (dentry->d_op && dentry->d_op->d_revalidate) + (dentry->d_op->d_revalidate)(dentry, nd); +- mutex_lock(&dir->i_mutex); +- } ++ mutex_lock(&dir->i_mutex); + } + + /* +--- linux-2.6.29.orig/include/linux/auto_dev-ioctl.h ++++ linux-2.6.29/include/linux/auto_dev-ioctl.h +@@ -10,8 +10,13 @@ + #ifndef _LINUX_AUTO_DEV_IOCTL_H + #define _LINUX_AUTO_DEV_IOCTL_H + ++#include ++ ++#ifdef __KERNEL__ + #include +-#include ++#else ++#include ++#endif /* __KERNEL__ */ + + #define AUTOFS_DEVICE_NAME "autofs" + +--- linux-2.6.29.orig/include/linux/auto_fs.h ++++ linux-2.6.29/include/linux/auto_fs.h +@@ -17,11 +17,13 @@ + #ifdef __KERNEL__ + #include + #include ++#include ++#include ++#else + #include ++#include + #endif /* __KERNEL__ */ + +-#include +- + /* This file describes autofs v3 */ + #define AUTOFS_PROTO_VERSION 3 + +--- linux-2.6.29.orig/fs/autofs4/waitq.c ++++ linux-2.6.29/fs/autofs4/waitq.c +@@ -297,20 +297,14 @@ static int validate_request(struct autof + */ + if (notify == NFY_MOUNT) { + /* +- * If the dentry isn't hashed just go ahead and try the +- * mount again with a new wait (not much else we can do). +- */ +- if (!d_unhashed(dentry)) { +- /* +- * But if the dentry is hashed, that means that we +- * got here through the revalidate path. Thus, we +- * need to check if the dentry has been mounted +- * while we waited on the wq_mutex. If it has, +- * simply return success. +- */ +- if (d_mountpoint(dentry)) +- return 0; +- } ++ * If the dentry was successfully mounted while we slept ++ * on the wait queue mutex we can return success. If it ++ * isn't mounted (doesn't have submounts for the case of ++ * a multi-mount with no mount at it's base) we can ++ * continue on and create a new request. ++ */ ++ if (have_submounts(dentry)) ++ return 0; + } + + return 1; diff --git a/patches/autofs4-2.6.9-v5-update-20081027.patch b/patches/autofs4-2.6.9-v5-update-20081027.patch deleted file mode 100644 index c08b3ab..0000000 --- a/patches/autofs4-2.6.9-v5-update-20081027.patch +++ /dev/null @@ -1,3119 +0,0 @@ -Index: linux-2.6.9/fs/autofs4/root.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/root.c -+++ linux-2.6.9/fs/autofs4/root.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,157 +19,61 @@ - #include - #include "autofs_i.h" - --static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *, struct nameidata *); - static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *); - static int autofs4_dir_unlink(struct inode *,struct dentry *); - static int autofs4_dir_rmdir(struct inode *,struct dentry *); - static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); - static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); - static int autofs4_dir_open(struct inode *inode, struct file *file); --static int autofs4_dir_close(struct inode *inode, struct file *file); --static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir); --static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); --static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *, struct nameidata *); --static int autofs4_dcache_readdir(struct file *, void *, filldir_t); -+static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); -+static int autofs4_follow_link(struct dentry *, struct nameidata *); -+ -+#define TRIGGER_FLAGS (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) -+#define TRIGGER_INTENTS (LOOKUP_OPEN | LOOKUP_CREATE) - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, - .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_root_readdir, -+ .readdir = dcache_readdir, - .ioctl = autofs4_root_ioctl, - }; - - struct file_operations autofs4_dir_operations = { - .open = autofs4_dir_open, -- .release = autofs4_dir_close, -+ .release = dcache_dir_close, - .read = generic_read_dir, -- .readdir = autofs4_dir_readdir, -+ .readdir = dcache_readdir, - }; - --struct inode_operations autofs4_root_inode_operations = { -- .lookup = autofs4_root_lookup, -+struct inode_operations autofs4_indirect_root_inode_operations = { -+ .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, - .mkdir = autofs4_dir_mkdir, - .rmdir = autofs4_dir_rmdir, - }; - -+struct inode_operations autofs4_direct_root_inode_operations = { -+ .lookup = autofs4_lookup, -+ .unlink = autofs4_dir_unlink, -+ .mkdir = autofs4_dir_mkdir, -+ .rmdir = autofs4_dir_rmdir, -+ .follow_link = autofs4_follow_link, -+}; -+ - struct inode_operations autofs4_dir_inode_operations = { -- .lookup = autofs4_dir_lookup, -+ .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .symlink = autofs4_dir_symlink, - .mkdir = autofs4_dir_mkdir, - .rmdir = autofs4_dir_rmdir, - }; - --static int autofs4_root_readdir(struct file *file, void *dirent, -- filldir_t filldir) --{ -- struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb); -- int oz_mode = autofs4_oz_mode(sbi); -- -- DPRINTK("called, filp->f_pos = %lld", file->f_pos); -- -- /* -- * Don't set reghost flag if: -- * 1) f_pos is larger than zero -- we've already been here. -- * 2) we haven't even enabled reghosting in the 1st place. -- * 3) this is the daemon doing a readdir -- */ -- if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) -- sbi->needs_reghost = 1; -- -- DPRINTK("needs_reghost = %d", sbi->needs_reghost); -- -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --/* Update usage from here to top of tree, so that scan of -- top-level directories will give a useful result */ --static void autofs4_update_usage(struct dentry *dentry) --{ -- struct dentry *top = dentry->d_sb->s_root; -- -- spin_lock(&dcache_lock); -- for(; dentry != top; dentry = dentry->d_parent) { -- struct autofs_info *ino = autofs4_dentry_ino(dentry); -- -- if (ino) { -- update_atime(dentry->d_inode); -- ino->last_used = jiffies; -- } -- } -- spin_unlock(&dcache_lock); --} -- --/* -- * From 2.4 kernel readdir.c -- */ --static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) --{ -- int i; -- struct dentry *dentry = filp->f_dentry; -- -- i = filp->f_pos; -- switch (i) { -- case 0: -- if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- case 1: -- if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) -- break; -- i++; -- filp->f_pos++; -- /* fallthrough */ -- default: { -- struct list_head *list; -- int j = i-2; -- -- spin_lock(&dcache_lock); -- list = dentry->d_subdirs.next; -- -- for (;;) { -- if (list == &dentry->d_subdirs) { -- spin_unlock(&dcache_lock); -- return 0; -- } -- if (!j) -- break; -- j--; -- list = list->next; -- } -- -- while(1) { -- struct dentry *de = list_entry(list, struct dentry, d_child); -- -- if (!d_unhashed(de) && de->d_inode) { -- spin_unlock(&dcache_lock); -- if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) -- break; -- spin_lock(&dcache_lock); -- } -- filp->f_pos++; -- list = list->next; -- if (list != &dentry->d_subdirs) -- continue; -- spin_unlock(&dcache_lock); -- break; -- } -- } -- } -- return 0; --} -- - static int autofs4_dir_open(struct inode *inode, struct file *file) - { - struct dentry *dentry = file->f_dentry; -- struct vfsmount *mnt = file->f_vfsmnt; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- int status; - - DPRINTK("file=%p dentry=%p %.*s", - file, dentry, dentry->d_name.len, dentry->d_name.name); -@@ -177,135 +81,31 @@ static int autofs4_dir_open(struct inode - if (autofs4_oz_mode(sbi)) - goto out; - -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { -- struct nameidata nd; -- int empty; -- -- /* In case there are stale directory dentrys from a failed mount */ -- spin_lock(&dcache_lock); -- empty = list_empty(&dentry->d_subdirs); -+ /* -+ * An empty directory in an autofs file system is always a -+ * mount point. The daemon must have failed to mount this -+ * during lookup so it doesn't exist. This can happen, for -+ * example, if user space returns an incorrect status for a -+ * mount request. Otherwise we're doing a readdir on the -+ * autofs file system so just let the libfs routines handle -+ * it. -+ */ -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); -- -- if (!empty) -- d_invalidate(dentry); -- -- nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -- -- if (!status) -- return -ENOENT; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = NULL; -- struct vfsmount *fp_mnt = mntget(mnt); -- struct dentry *fp_dentry = dget(dentry); -- -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); -- -- fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); -- status = PTR_ERR(fp); -- if (IS_ERR(fp)) { -- file->private_data = NULL; -- return status; -- } -- file->private_data = fp; -- } --out: -- return 0; --} -- --static int autofs4_dir_close(struct inode *inode, struct file *file) --{ -- struct dentry *dentry = file->f_dentry; -- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -- -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -+ return -ENOENT; - } -+ spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- filp_close(fp, current->files); -- file->private_data = NULL; -- } - out: -- return 0; -+ return dcache_dir_open(inode, file); - } - --static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct dentry *dentry = file->f_dentry; - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - int status; - -- DPRINTK("file=%p dentry=%p %.*s", -- file, dentry, dentry->d_name.len, dentry->d_name.name); -- -- if (autofs4_oz_mode(sbi)) -- goto out; -- -- if (autofs4_ispending(dentry)) { -- DPRINTK("dentry busy"); -- return -EBUSY; -- } -- -- if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -- if (!fp->f_op || !fp->f_op->readdir) -- goto out; -- -- status = vfs_readdir(fp, filldir, dirent); -- file->f_pos = fp->f_pos; -- if (status) -- autofs4_copy_atime(file, fp); -- return status; -- } --out: -- return autofs4_dcache_readdir(file, dirent, filldir); --} -- --static int try_to_fill_dentry(struct dentry *dentry, -- struct super_block *sb, -- struct autofs_sb_info *sbi, int flags) --{ -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -- int status = 0; -- -- /* Block on any pending expiry here; invalidate the dentry -- when expiration is done to trigger mount request with a new -- dentry */ -- if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -- DPRINTK("waiting for expire %p name=%.*s", -- dentry, dentry->d_name.len, dentry->d_name.name); -- -- status = autofs4_wait(sbi, dentry, NFY_NONE); -- -- DPRINTK("expire done status=%d", status); -- -- return 0; -- } -- - DPRINTK("dentry=%p %.*s ino=%p", - dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); - -@@ -318,22 +118,19 @@ static int try_to_fill_dentry(struct den - - DPRINTK("mount done status=%d", status); - -- if (status && dentry->d_inode) -- return 0; /* Try to get the kernel to invalidate this dentry */ -- - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { -- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ return status; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ -- return 1; -+ return status; - } - /* Trigger mount for path component or follow link */ -- } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || -+ } else if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ flags & (TRIGGER_FLAGS | TRIGGER_INTENTS) || - current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); -@@ -349,19 +146,96 @@ static int try_to_fill_dentry(struct den - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 0; -+ return status; - } - } - -- /* We don't update the usages for the autofs daemon itself, this -- is necessary for recursive autofs mounts */ -- if (!autofs4_oz_mode(sbi)) -- autofs4_update_usage(dentry); -+ /* Initialize expiry counter after successful mount */ -+ if (ino) -+ ino->last_used = jiffies; - - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- return 1; -+ -+ return 0; -+} -+ -+/* For autofs direct mounts the follow link triggers the mount */ -+static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int oz_mode = autofs4_oz_mode(sbi); -+ unsigned int lookup_type; -+ int status; -+ -+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d", -+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode, -+ nd->flags); -+ /* -+ * For an expire of a covered direct or offset mount we need -+ * to beeak out of follow_down() at the autofs mount trigger -+ * (d_mounted--), so we can see the expiring flag, and manage -+ * the blocking and following here until the expire is completed. -+ */ -+ if (oz_mode) { -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ /* Follow down to our covering mount. */ -+ if (!follow_down(&nd->mnt, &nd->dentry)) -+ goto done; -+ goto follow; -+ } -+ spin_unlock(&sbi->fs_lock); -+ goto done; -+ } -+ -+ /* If an expire request is pending everyone must wait. */ -+ autofs4_expire_wait(dentry); -+ -+ /* We trigger a mount for almost all flags */ -+ lookup_type = nd->flags & (TRIGGER_FLAGS | TRIGGER_INTENTS); -+ if (!(lookup_type || dentry->d_flags & DCACHE_AUTOFS_PENDING)) -+ goto follow; -+ -+ /* -+ * If the dentry contains directories then it is an autofs -+ * multi-mount with no root mount offset. So don't try to -+ * mount it again. -+ */ -+ spin_lock(&dcache_lock); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING || -+ (!d_mountpoint(dentry) && __simple_empty(dentry))) { -+ spin_unlock(&dcache_lock); -+ -+ status = try_to_fill_dentry(dentry, 0); -+ if (status) -+ goto out_error; -+ -+ goto follow; -+ } -+ spin_unlock(&dcache_lock); -+follow: -+ /* -+ * If there is no root mount it must be an autofs -+ * multi-mount with no root offset so we don't need -+ * to follow it. -+ */ -+ if (d_mountpoint(dentry)) { -+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) { -+ status = -ENOENT; -+ goto out_error; -+ } -+ } -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -370,47 +244,76 @@ static int try_to_fill_dentry(struct den - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ --static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd) -+static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd) - { -- struct inode * dir = dentry->d_parent->d_inode; -+ struct inode *dir = dentry->d_parent->d_inode; - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - int oz_mode = autofs4_oz_mode(sbi); - int flags = nd ? nd->flags : 0; -- int status = 1; -+ int status; - - /* Pending dentry */ -+ spin_lock(&sbi->fs_lock); - if (autofs4_ispending(dentry)) { -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ /* The daemon never causes a mount to trigger */ -+ spin_unlock(&sbi->fs_lock); -+ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * If the directory has gone away due to an expire -+ * we have been called as ->d_revalidate() and so -+ * we need to return false and proceed to ->lookup(). -+ */ -+ if (autofs4_expire_wait(dentry) == -EAGAIN) -+ return 0; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } -+ spin_unlock(&sbi->fs_lock); - - /* Negative dentry.. invalidate if "old" */ - if (dentry->d_inode == NULL) -- return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); -+ return 0; - - /* Check for a non-mountpoint directory with no contents */ - spin_lock(&dcache_lock); - if (S_ISDIR(dentry->d_inode->i_mode) && - !d_mountpoint(dentry) && -- list_empty(&dentry->d_subdirs)) { -+ __simple_empty(dentry)) { - DPRINTK("dentry=%p %.*s, emptydir", - dentry, dentry->d_name.len, dentry->d_name.name); - spin_unlock(&dcache_lock); -- if (!oz_mode) -- status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags); -+ -+ /* The daemon never causes a mount to trigger */ -+ if (oz_mode) -+ return 1; -+ -+ /* -+ * A zero status is success otherwise we have a -+ * negative error code. -+ */ -+ status = try_to_fill_dentry(dentry, flags); -+ if (status == 0) -+ return 1; -+ - return status; - } - spin_unlock(&dcache_lock); - -- /* Update the usage list */ -- if (!oz_mode) -- autofs4_update_usage(dentry); -- - return 1; - } - --static void autofs4_dentry_release(struct dentry *de) -+void autofs4_dentry_release(struct dentry *de) - { - struct autofs_info *inf; - -@@ -420,6 +323,17 @@ static void autofs4_dentry_release(struc - de->d_fsdata = NULL; - - if (inf) { -+ struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); -+ -+ if (sbi) { -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&inf->active)) -+ list_del(&inf->active); -+ if (!list_empty(&inf->expiring)) -+ list_del(&inf->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -439,63 +353,192 @@ static struct dentry_operations autofs4_ - .d_release = autofs4_dentry_release, - }; - --/* Lookups in non-root dirs never find anything - if it's there, it's -- already in the dcache */ --static struct dentry *autofs4_dir_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) --{ --#if 0 -- DPRINTK("ignoring lookup of %.*s/%.*s", -- dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, -- dentry->d_name.len, dentry->d_name.name); --#endif -+static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->active_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, active); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Already gone? */ -+ if (atomic_read(&dentry->d_count) == 0) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ -+ return NULL; -+} -+ -+static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) -+{ -+ unsigned int len = name->len; -+ unsigned int hash = name->hash; -+ const unsigned char *str = name->name; -+ struct list_head *p, *head; -+ -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ head = &sbi->expiring_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, expiring); -+ dentry = ino->dentry; -+ -+ spin_lock(&dentry->d_lock); -+ -+ /* Bad luck, we've already been dentry_iput */ -+ if (!dentry->d_inode) -+ goto next; -+ -+ qstr = &dentry->d_name; -+ -+ if (dentry->d_name.hash != hash) -+ goto next; -+ if (dentry->d_parent != parent) -+ goto next; -+ -+ if (qstr->len != len) -+ goto next; -+ if (memcmp(qstr->name, str, len)) -+ goto next; -+ -+ if (d_unhashed(dentry)) { -+ dget(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->lookup_lock); -+ spin_unlock(&dcache_lock); - -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); - return NULL; - } - - /* Lookups in the root directory */ --static struct dentry *autofs4_root_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -+static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) - { - struct autofs_sb_info *sbi; -+ struct autofs_info *ino; -+ struct dentry *expiring, *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", - dentry->d_name.len, dentry->d_name.name); - -+ /* File name too long to exist */ - if (dentry->d_name.len > NAME_MAX) -- return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ -+ return ERR_PTR(-ENAMETOOLONG); - - sbi = autofs4_sbi(dir->i_sb); -- - oz_mode = autofs4_oz_mode(sbi); -+ - DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", - current->pid, process_group(current), sbi->catatonic, oz_mode); - -- /* -- * Mark the dentry incomplete, but add it. This is needed so -- * that the VFS layer knows about the dentry, and we can count -- * on catching any lookups through the revalidate. -- * -- * Let all the hard work be done by the revalidate function that -- * needs to be able to do this anyway.. -- * -- * We need to do this before we release the directory semaphore. -- */ -- dentry->d_op = &autofs4_root_dentry_operations; -+ expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); -+ if (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, so just wait for it. -+ */ -+ ino = autofs4_dentry_ino(expiring); -+ autofs4_expire_wait(expiring); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->expiring)) -+ list_del_init(&ino->expiring); -+ spin_unlock(&sbi->lookup_lock); -+ dput(expiring); -+ } -+ -+ unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); -+ if (unhashed) -+ dentry = unhashed; -+ else { -+ /* -+ * Mark the dentry incomplete but don't hash it. We do this -+ * to serialize our inode creation operations (symlink and -+ * mkdir) which prevents deadlock during the callback to -+ * the daemon. Subsequent user space lookups for the same -+ * dentry are placed on the wait queue while the daemon -+ * itself is allowed passage unresticted so the create -+ * operation itself can then hash the dentry. Finally, -+ * we check for the hashed dentry and return the newly -+ * hashed dentry. -+ */ -+ dentry->d_op = &autofs4_root_dentry_operations; -+ -+ /* -+ * And we need to ensure that the same dentry is used for -+ * all following lookup calls until it is hashed so that -+ * the dentry flags are persistent throughout the request. -+ */ -+ ino = autofs4_init_ino(NULL, sbi, 0555); -+ if (!ino) -+ return ERR_PTR(-ENOMEM); -+ -+ dentry->d_fsdata = ino; -+ ino->dentry = dentry; -+ -+ spin_lock(&sbi->lookup_lock); -+ list_add(&ino->active, &sbi->active_list); -+ spin_unlock(&sbi->lookup_lock); -+ -+ d_instantiate(dentry, NULL); -+ } - - if (!oz_mode) { - spin_lock(&dentry->d_lock); - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - spin_unlock(&dentry->d_lock); -- } -- dentry->d_fsdata = NULL; -- d_add(dentry, NULL); -- -- if (dentry->d_op && dentry->d_op->d_revalidate) { -- up(&dir->i_sem); -- (dentry->d_op->d_revalidate)(dentry, nd); -- down(&dir->i_sem); -+ if (dentry->d_op && dentry->d_op->d_revalidate) { -+ up(&dir->i_sem); -+ (dentry->d_op->d_revalidate)(dentry, nd); -+ down(&dir->i_sem); -+ } - } - - /* -@@ -509,19 +552,47 @@ static struct dentry *autofs4_root_looku - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -+ if (!oz_mode) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; -+ spin_unlock(&dentry->d_lock); -+ } - } - - /* - * If this dentry is unhashed, then we shouldn't honour this -- * lookup even if the dentry is positive. Returning ENOENT here -- * doesn't do the right thing for all system calls, but it should -- * be OK for the operations we permit from an autofs. -+ * lookup. Returning ENOENT here doesn't do the right thing -+ * for all system calls, but it should be OK for the operations -+ * we permit from an autofs. - */ -- if ( dentry->d_inode && d_unhashed(dentry) ) -- return ERR_PTR(-ENOENT); -+ if (!oz_mode && d_unhashed(dentry)) { -+ /* -+ * A user space application can (and has done in the past) -+ * remove and re-create this directory during the callback. -+ * This can leave us with an unhashed dentry, but a -+ * successful mount! So we need to perform another -+ * cached lookup in case the dentry now exists. -+ */ -+ struct dentry *parent = dentry->d_parent; -+ struct dentry *new = d_lookup(parent, &dentry->d_name); -+ if (new != NULL) -+ dentry = new; -+ else -+ dentry = ERR_PTR(-ENOENT); -+ -+ if (unhashed) -+ dput(unhashed); -+ -+ return dentry; -+ } -+ -+ if (unhashed) -+ return unhashed; - - return NULL; - } -@@ -532,6 +603,7 @@ static int autofs4_dir_symlink(struct in - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - char *cp; - -@@ -542,21 +614,32 @@ static int autofs4_dir_symlink(struct in - return -EACCES; - - ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; - -- ino->size = strlen(symname); -- ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - -- if (cp == NULL) { -- kfree(ino); -- return -ENOSPC; -+ ino->size = strlen(symname); -+ cp = kmalloc(ino->size + 1, GFP_KERNEL); -+ if (!cp) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; - } - - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ kfree(cp); -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -565,8 +648,13 @@ static int autofs4_dir_symlink(struct in - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - -+ ino->u.symlink = cp; - dir->i_mtime = CURRENT_TIME; - - return 0; -@@ -578,9 +666,9 @@ static int autofs4_dir_symlink(struct in - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry. We don't want -- * this, because since the unlink is probably the result of an expire. -- * We simply d_drop it, which allows the dentry lookup to remount it -- * if necessary. -+ * this, because the unlink is probably the result of an expire. -+ * We simply d_drop it and add it to a expiring list in the super block, -+ * which allows the dentry lookup to check for an incomplete expire. - * - * If a process is blocked on the dentry waiting for the expire to finish, - * it will invalidate the dentry and try to mount with a new one. -@@ -591,11 +679,17 @@ static int autofs4_dir_unlink(struct ino - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - - /* This allows root to remove symlinks */ - if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) - return -EACCES; - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); - - dentry->d_inode->i_size = 0; -@@ -603,7 +697,13 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -612,7 +712,11 @@ static int autofs4_dir_rmdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - -+ DPRINTK("dentry %p, removing %.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ - if (!autofs4_oz_mode(sbi)) - return -EACCES; - -@@ -621,11 +725,19 @@ static int autofs4_dir_rmdir(struct inod - spin_unlock(&dcache_lock); - return -ENOTEMPTY; - } -+ spin_lock(&sbi->lookup_lock); -+ if (list_empty(&ino->expiring)) -+ list_add(&ino->expiring, &sbi->expiring_list); -+ spin_unlock(&sbi->lookup_lock); - __d_drop(dentry); - spin_unlock(&dcache_lock); - -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -- - dentry->d_inode->i_size = 0; - dentry->d_inode->i_nlink = 0; - -@@ -639,6 +751,7 @@ static int autofs4_dir_mkdir(struct inod - { - struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ struct autofs_info *p_ino; - struct inode *inode; - - if ( !autofs4_oz_mode(sbi) ) -@@ -648,11 +761,21 @@ static int autofs4_dir_mkdir(struct inod - dentry, dentry->d_name.len, dentry->d_name.name); - - ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); -- if (ino == NULL) -- return -ENOSPC; -+ if (!ino) -+ return -ENOMEM; -+ -+ spin_lock(&sbi->lookup_lock); -+ if (!list_empty(&ino->active)) -+ list_del_init(&ino->active); -+ spin_unlock(&sbi->lookup_lock); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ if (!inode) { -+ if (!dentry->d_fsdata) -+ kfree(ino); -+ return -ENOMEM; -+ } -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -661,6 +784,10 @@ static int autofs4_dir_mkdir(struct inod - - dentry->d_fsdata = ino; - ino->dentry = dget(dentry); -+ atomic_inc(&ino->count); -+ p_ino = autofs4_dentry_ino(dentry->d_parent); -+ if (p_ino && dentry->d_parent != dentry) -+ atomic_inc(&p_ino->count); - ino->inode = inode; - dir->i_nlink++; - dir->i_mtime = CURRENT_TIME; -@@ -700,51 +827,13 @@ static inline int autofs4_get_protosubve - } - - /* -- * Tells the daemon whether we need to reghost or not. Also, clears -- * the reghost_needed flag. -- */ --static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- -- DPRINTK("returning %d", sbi->needs_reghost); -- -- status = put_user(sbi->needs_reghost, p); -- if ( status ) -- return status; -- -- sbi->needs_reghost = 0; -- return 0; --} -- --/* -- * Enable / Disable reghosting ioctl() operation -- */ --static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) --{ -- int status; -- int val; -- -- status = get_user(val, p); -- -- DPRINTK("reghost = %d", val); -- -- if (status) -- return status; -- -- /* turn on/off reghosting, with the val */ -- sbi->reghost_enabled = val; -- return 0; --} -- --/* - * Tells the daemon whether it can umount the autofs mount. - */ - static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -@@ -801,11 +890,6 @@ static int autofs4_root_ioctl(struct ino - case AUTOFS_IOC_SETTIMEOUT: - return autofs4_get_set_timeout(sbi, p); - -- case AUTOFS_IOC_TOGGLEREGHOST: -- return autofs4_toggle_reghost(sbi, p); -- case AUTOFS_IOC_ASKREGHOST: -- return autofs4_ask_reghost(sbi, p); -- - case AUTOFS_IOC_ASKUMOUNT: - return autofs4_ask_umount(filp->f_vfsmnt, p); - -Index: linux-2.6.9/fs/autofs4/autofs_i.h -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/autofs_i.h -+++ linux-2.6.9/fs/autofs4/autofs_i.h -@@ -3,6 +3,7 @@ - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -19,6 +20,8 @@ - #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY - #define AUTOFS_IOC_COUNT 32 - -+#define AUTOFS_TYPE_TRIGGER (AUTOFS_TYPE_DIRECT|AUTOFS_TYPE_OFFSET) -+ - #include - #include - #include -@@ -40,14 +43,6 @@ - - #define AUTOFS_SUPER_MAGIC 0x0187 - --/* -- * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the -- * kernel will keep the negative response cached for up to the time given -- * here, although the time can be shorter if the kernel throws the dcache -- * entry away. This probably should be settable from user space. -- */ --#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ -- - /* Unified info structure. This is pointed to by both the dentry and - inode structures. Each file in the filesystem has an instance of this - structure. It holds a reference to the dentry, so dentries are never -@@ -60,8 +55,14 @@ struct autofs_info { - - int flags; - -+ struct completion expire_complete; -+ -+ struct list_head active; -+ struct list_head expiring; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -73,35 +74,48 @@ struct autofs_info { - }; - - #define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ -+#define AUTOFS_INF_MOUNTPOINT (1<<1) /* mountpoint status for direct expire */ - - struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ -- int hash; -- int len; -- char *name; -+ struct qstr name; -+ u32 dev; -+ u64 ino; -+ uid_t uid; -+ gid_t gid; -+ pid_t pid; -+ pid_t tgid; - /* This is for status reporting upon return */ - int status; -- atomic_t wait_ctr; -+ unsigned int wait_ctr; - }; - - #define AUTOFS_SBI_MAGIC 0x6d4a556d - - struct autofs_sb_info { - u32 magic; -+ int pipefd; - struct file *pipe; - pid_t oz_pgrp; - int catatonic; - int version; - int sub_version; -+ int min_proto; -+ int max_proto; - unsigned long exp_timeout; -+ unsigned int type; - int reghost_enabled; - int needs_reghost; - struct super_block *sb; - struct semaphore wq_sem; -+ spinlock_t fs_lock; - struct autofs_wait_queue *queues; /* Wait queue pointer */ -+ spinlock_t lookup_lock; -+ struct list_head active_list; -+ struct list_head expiring_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -127,8 +141,13 @@ static inline int autofs4_ispending(stru - { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) -+ return 1; -+ -+ if (inf->flags & AUTOFS_INF_EXPIRING) -+ return 1; -+ -+ return 0; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -142,6 +161,7 @@ void autofs4_free_ino(struct autofs_info - - /* Expiration */ - int is_autofs4_dentry(struct dentry *); -+int autofs4_expire_wait(struct dentry *dentry); - int autofs4_expire_run(struct super_block *, struct vfsmount *, - struct autofs_sb_info *, - struct autofs_packet_expire __user *); -@@ -153,6 +173,8 @@ int autofs4_expire_multi(struct super_bl - extern struct inode_operations autofs4_symlink_inode_operations; - extern struct inode_operations autofs4_dir_inode_operations; - extern struct inode_operations autofs4_root_inode_operations; -+extern struct inode_operations autofs4_indirect_root_inode_operations; -+extern struct inode_operations autofs4_direct_root_inode_operations; - extern struct file_operations autofs4_dir_operations; - extern struct file_operations autofs4_root_operations; - -@@ -163,23 +185,39 @@ struct autofs_info *autofs4_init_ino(str - - /* Queue management functions */ - --enum autofs_notify --{ -- NFY_NONE, -- NFY_MOUNT, -- NFY_EXPIRE --}; -- - int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify); - int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); - void autofs4_catatonic_mode(struct autofs_sb_info *); - -+static inline int autofs4_follow_mount(struct vfsmount **mnt, struct dentry **dentry) -+{ -+ int res = 0; -+ -+ while (d_mountpoint(*dentry)) { -+ int followed = follow_down(mnt, dentry); -+ if (!followed) -+ break; -+ res = 1; -+ } -+ return res; -+} -+ -+static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi) -+{ -+ return new_encode_dev(sbi->sb->s_dev); -+} -+ -+static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) -+{ -+ return sbi->sb->s_root->d_inode->i_ino; -+} -+ - static inline int simple_positive(struct dentry *dentry) - { - return dentry->d_inode && !d_unhashed(dentry); - } - --static inline int simple_empty_nolock(struct dentry *dentry) -+static inline int __simple_empty(struct dentry *dentry) - { - struct dentry *child; - int ret = 0; -@@ -191,3 +229,6 @@ static inline int simple_empty_nolock(st - out: - return ret; - } -+ -+void autofs4_dentry_release(struct dentry *); -+extern void autofs4_kill_sb(struct super_block *); -Index: linux-2.6.9/fs/autofs4/expire.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/expire.c -+++ linux-2.6.9/fs/autofs4/expire.c -@@ -4,7 +4,7 @@ - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * Copyright 1999-2000 Jeremy Fitzhardinge -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -16,7 +16,7 @@ - - static unsigned long now; - --/* Check if a dentry can be expired return 1 if it can else return 0 */ -+/* Check if a dentry can be expired */ - static inline int autofs4_can_expire(struct dentry *dentry, - unsigned long timeout, int do_now) - { -@@ -41,14 +41,14 @@ static inline int autofs4_can_expire(str - attempts if expire fails the first time */ - ino->last_used = now; - } -- - return 1; - } - --/* Check a mount point for busyness return 1 if not busy, otherwise */ --static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) -+/* Check a mount point for busyness */ -+static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) - { -- int status = 0; -+ struct dentry *top = dentry; -+ int status = 1; - - DPRINTK("dentry %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); -@@ -59,91 +59,160 @@ static int autofs4_check_mount(struct vf - if (!follow_down(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -+ if (is_autofs4_dentry(dentry)) { -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ -+ /* This is an autofs submount, we can't expire it */ -+ if (sbi->type == AUTOFS_TYPE_INDIRECT) -+ goto done; -+ -+ /* -+ * Otherwise it's an offset mount and we need to check -+ * if we can umount its mount, if there is one. -+ */ -+ if (!d_mountpoint(dentry)) -+ goto done; -+ } - -- /* This is an autofs submount, we can't expire it */ -- if (is_autofs4_dentry(dentry)) -+ /* Update the expiry counter if fs is busy */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ ino->last_used = jiffies; - goto done; -+ } - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ status = 0; - done: - DPRINTK("returning = %d", status); -- mntput(mnt); - dput(dentry); -+ mntput(mnt); - return status; - } - -+/* -+ * Calculate next entry in top down tree traversal. -+ * From next_mnt in namespace.c - elegant. -+ */ -+static struct dentry *next_dentry(struct dentry *p, struct dentry *root) -+{ -+ struct list_head *next = p->d_subdirs.next; -+ -+ if (next == &p->d_subdirs) { -+ while (1) { -+ if (p == root) -+ return NULL; -+ next = p->d_child.next; -+ if (next != &p->d_parent->d_subdirs) -+ break; -+ p = p->d_parent; -+ } -+ } -+ return list_entry(next, struct dentry, d_child); -+} -+ -+/* -+ * Check a direct mount point for busyness. -+ * Direct mounts have similar expiry semantics to tree mounts. -+ * The tree is not busy iff no mountpoints are busy and there are no -+ * autofs submounts. -+ */ -+static int autofs4_direct_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) -+{ -+ DPRINTK("top %p %.*s", -+ top, (int) top->d_name.len, top->d_name.name); -+ -+ /* If it's busy update the expiry counters */ -+ if (!may_umount_tree(mnt)) { -+ struct autofs_info *ino = autofs4_dentry_ino(top); -+ if (ino) -+ ino->last_used = jiffies; -+ return 1; -+ } -+ -+ /* Timeout of a direct mount is determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; -+} -+ - /* Check a directory tree of mount points for busyness - * The tree is not busy iff no mountpoints are busy -- * Return 1 if the tree is busy or 0 otherwise - */ --static int autofs4_check_tree(struct vfsmount *mnt, -- struct dentry *top, -- unsigned long timeout, -- int do_now) -+static int autofs4_tree_busy(struct vfsmount *mnt, -+ struct dentry *top, -+ unsigned long timeout, -+ int do_now) - { -- struct dentry *this_parent = top; -- struct list_head *next; -+ struct autofs_info *top_ino = autofs4_dentry_ino(top); -+ struct dentry *p; - -- DPRINTK("parent %p %.*s", -+ DPRINTK("top %p %.*s", - top, (int)top->d_name.len, top->d_name.name); - - /* Negative dentry - give up */ - if (!simple_positive(top)) -- return 0; -- -- /* Timeout of a tree mount is determined by its top dentry */ -- if (!autofs4_can_expire(top, timeout, do_now)) -- return 0; -+ return 1; - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = top; p; p = next_dentry(p, top)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -- -- if (!simple_empty_nolock(dentry)) { -- this_parent = dentry; -- goto repeat; -- } -+ p, (int) p->d_name.len, p->d_name.name); - -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* First busy => tree busy */ -- if (!autofs4_check_mount(mnt, dentry)) { -- dput(dentry); -- return 0; -+ /* -+ * Is someone visiting anywhere in the subtree ? -+ * If there's no mount we need to check the usage -+ * count for the autofs dentry. -+ * If the fs is busy update the expiry counter. -+ */ -+ if (d_mountpoint(p)) { -+ if (autofs4_mount_busy(mnt, p)) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; - } -- } -+ } else { -+ struct autofs_info *ino = autofs4_dentry_ino(p); -+ unsigned int ino_count = atomic_read(&ino->count); - -- dput(dentry); -+ /* -+ * Clean stale dentries below that have not been -+ * invalidated after a mount fail during lookup -+ */ -+ d_invalidate(p); -+ -+ /* allow for dget above and top is already dgot */ -+ if (p == top) -+ ino_count += 2; -+ else -+ ino_count++; -+ -+ if (atomic_read(&p->d_count) > ino_count) { -+ top_ino->last_used = jiffies; -+ dput(p); -+ return 1; -+ } -+ } -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; -- } -- -- if (this_parent != top) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; - } - spin_unlock(&dcache_lock); - -- return 1; -+ /* Timeout of a tree mount is ultimately determined by its top dentry */ -+ if (!autofs4_can_expire(top, timeout, do_now)) -+ return 1; -+ -+ return 0; - } - - static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, -@@ -151,58 +220,70 @@ static struct dentry *autofs4_check_leav - unsigned long timeout, - int do_now) - { -- struct dentry *this_parent = parent; -- struct list_head *next; -+ struct dentry *p; - - DPRINTK("parent %p %.*s", - parent, (int)parent->d_name.len, parent->d_name.name); - - spin_lock(&dcache_lock); --repeat: -- next = this_parent->d_subdirs.next; --resume: -- while (next != &this_parent->d_subdirs) { -- struct dentry *dentry = list_entry(next, struct dentry, d_child); -- -+ for (p = parent; p; p = next_dentry(p, parent)) { - /* Negative dentry - give up */ -- if (!simple_positive(dentry)) { -- next = next->next; -+ if (!simple_positive(p)) - continue; -- } - - DPRINTK("dentry %p %.*s", -- dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ p, (int) p->d_name.len, p->d_name.name); - -- if (!list_empty(&dentry->d_subdirs)) { -- this_parent = dentry; -- goto repeat; -- } -- -- dentry = dget(dentry); -+ p = dget(p); - spin_unlock(&dcache_lock); - -- if (d_mountpoint(dentry)) { -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -- goto cont; -- -+ if (d_mountpoint(p)) { - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) -- return dentry; -+ if (autofs4_mount_busy(mnt, p)) -+ goto cont; - -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(p, timeout, do_now)) -+ return p; - } - cont: -- dput(dentry); -+ dput(p); - spin_lock(&dcache_lock); -- next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; -+} -+ -+/* Check if we can expire a direct mount (possibly a tree) */ -+static struct dentry *autofs4_expire_direct(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) -+{ -+ unsigned long timeout; -+ struct dentry *root = dget(sb->s_root); -+ int do_now = how & AUTOFS_EXP_IMMEDIATE; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ if (!sbi->exp_timeout || !root) -+ return NULL; -+ -+ now = jiffies; -+ timeout = sbi->exp_timeout; -+ -+ spin_lock(&sbi->fs_lock); -+ if (!autofs4_direct_busy(mnt, root, timeout, do_now)) { -+ struct autofs_info *ino = autofs4_dentry_ino(root); -+ if (d_mountpoint(root)) { -+ ino->flags |= AUTOFS_INF_MOUNTPOINT; -+ root->d_mounted--; -+ } -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +294,10 @@ cont: - * - it is unused by any user process - * - it has been unused for exp_timeout time - */ --static struct dentry *autofs4_expire(struct super_block *sb, -- struct vfsmount *mnt, -- struct autofs_sb_info *sbi, -- int how) -+static struct dentry *autofs4_expire_indirect(struct super_block *sb, -+ struct vfsmount *mnt, -+ struct autofs_sb_info *sbi, -+ int how) - { - unsigned long timeout; - struct dentry *root = sb->s_root; -@@ -224,6 +305,8 @@ static struct dentry *autofs4_expire(str - struct list_head *next; - int do_now = how & AUTOFS_EXP_IMMEDIATE; - int exp_leaves = how & AUTOFS_EXP_LEAVES; -+ struct autofs_info *ino; -+ unsigned int ino_count; - - if ( !sbi->exp_timeout || !root ) - return NULL; -@@ -240,7 +323,7 @@ static struct dentry *autofs4_expire(str - struct dentry *dentry = list_entry(next, struct dentry, d_child); - - /* Negative dentry - give up */ -- if ( !simple_positive(dentry) ) { -+ if (!simple_positive(dentry)) { - next = next->next; - continue; - } -@@ -248,58 +331,116 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ -+ /* -+ * Case 1: (i) indirect mount or top level pseudo direct mount -+ * (autofs-4.1). -+ * (ii) indirect mount with offset mount, check the "/" -+ * offset (autofs-5.0+). -+ */ - if (d_mountpoint(dentry)) { - DPRINTK("checking mountpoint %p %.*s", - dentry, (int)dentry->d_name.len, dentry->d_name.name); - -- /* Can we expire this guy */ -- if (!autofs4_can_expire(dentry, timeout, do_now)) -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 2; -+ if (atomic_read(&dentry->d_count) > ino_count) - goto next; - - /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ if (autofs4_mount_busy(mnt, dentry)) -+ goto next; -+ -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; -- break; -+ goto found; - } - goto next; - } - -- if ( simple_empty(dentry) ) -+ if (simple_empty(dentry)) - goto next; - - /* Case 2: tree mount, expire iff entire tree is not busy */ - if (!exp_leaves) { -- if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { -- expired = dentry; -- break; -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { -+ expired = dentry; -+ goto found; - } -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { -+ /* Path walk currently on this dentry? */ -+ ino_count = atomic_read(&ino->count) + 1; -+ if (atomic_read(&dentry->d_count) > ino_count) -+ goto next; -+ - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { - dput(dentry); -- break; -+ goto found; - } - } - next: -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - spin_lock(&dcache_lock); - next = next->next; - } -+ spin_unlock(&dcache_lock); -+ return NULL; - -- if ( expired ) { -- DPRINTK("returning %p %.*s", -- expired, (int)expired->d_name.len, expired->d_name.name); -- spin_lock(&dcache_lock); -- list_del(&expired->d_parent->d_subdirs); -- list_add(&expired->d_parent->d_subdirs, &expired->d_child); -- spin_unlock(&dcache_lock); -- return expired; -- } -+found: -+ DPRINTK("returning %p %.*s", -+ expired, (int)expired->d_name.len, expired->d_name.name); -+ ino = autofs4_dentry_ino(expired); -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ init_completion(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ spin_lock(&dcache_lock); -+ list_del(&expired->d_parent->d_subdirs); -+ list_add(&expired->d_parent->d_subdirs, &expired->d_child); - spin_unlock(&dcache_lock); -+ return expired; -+} - -- return NULL; -+int autofs4_expire_wait(struct dentry *dentry) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); -+ int status; -+ -+ /* Block on any pending expire */ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_EXPIRING) { -+ spin_unlock(&sbi->fs_lock); -+ -+ DPRINTK("waiting for expire %p name=%.*s", -+ dentry, dentry->d_name.len, dentry->d_name.name); -+ -+ status = autofs4_wait(sbi, dentry, NFY_NONE); -+ wait_for_completion(&ino->expire_complete); -+ -+ DPRINTK("expire done status=%d", status); -+ -+ if (d_unhashed(dentry)) -+ return -EAGAIN; -+ -+ return status; -+ } -+ spin_unlock(&sbi->fs_lock); -+ -+ return 0; - } - - /* Perform an expiry operation */ -@@ -309,14 +450,16 @@ int autofs4_expire_run(struct super_bloc - struct autofs_packet_expire __user *pkt_p) - { - struct autofs_packet_expire pkt; -+ struct autofs_info *ino; - struct dentry *dentry; -+ int ret = 0; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = autofs_ptype_expire; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) -+ if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) - return -EAGAIN; - - pkt.len = dentry->d_name.len; -@@ -325,9 +468,15 @@ int autofs4_expire_run(struct super_bloc - dput(dentry); - - if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) -- return -EFAULT; -+ ret = -EFAULT; - -- return 0; -+ spin_lock(&sbi->fs_lock); -+ ino = autofs4_dentry_ino(dentry); -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); -+ -+ return ret; - } - - /* Call repeatedly until it returns -EAGAIN, meaning there's nothing -@@ -342,17 +491,29 @@ int autofs4_expire_multi(struct super_bl - if (arg && get_user(do_now, arg)) - return -EFAULT; - -- if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ dentry = autofs4_expire_direct(sb, mnt, sbi, do_now); -+ else -+ dentry = autofs4_expire_indirect(sb, mnt, sbi, do_now); -+ -+ if (dentry) { -+ struct autofs_info *ino = autofs4_dentry_ino(dentry); - - /* This is synchronous because it makes the daemon a - little easier */ -- de_info->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ -+ spin_lock(&sbi->fs_lock); -+ if (ino->flags & AUTOFS_INF_MOUNTPOINT) { -+ sb->s_root->d_mounted++; -+ ino->flags &= ~AUTOFS_INF_MOUNTPOINT; -+ } -+ ino->flags &= ~AUTOFS_INF_EXPIRING; -+ complete_all(&ino->expire_complete); -+ spin_unlock(&sbi->fs_lock); - dput(dentry); - } -- -+ - return ret; - } - -Index: linux-2.6.9/fs/autofs4/inode.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/inode.c -+++ linux-2.6.9/fs/autofs4/inode.c -@@ -3,6 +3,7 @@ - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -+ * Copyright 2005-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -13,9 +14,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - #include "autofs_i.h" - #include - -@@ -40,12 +43,17 @@ struct autofs_info *autofs4_init_ino(str - if (ino == NULL) - return NULL; - -- ino->flags = 0; -- ino->mode = mode; -- ino->inode = NULL; -- ino->dentry = NULL; -- ino->size = 0; -+ if (!reinit) { -+ ino->flags = 0; -+ ino->inode = NULL; -+ ino->dentry = NULL; -+ ino->size = 0; -+ INIT_LIST_HEAD(&ino->active); -+ INIT_LIST_HEAD(&ino->expiring); -+ atomic_set(&ino->count, 0); -+ } - -+ ino->mode = mode; - ino->last_used = jiffies; - - ino->sbi = sbi; -@@ -65,10 +73,19 @@ struct autofs_info *autofs4_init_ino(str - - void autofs4_free_ino(struct autofs_info *ino) - { -+ struct autofs_info *p_ino; -+ - if (ino->dentry) { - ino->dentry->d_fsdata = NULL; -- if (ino->dentry->d_inode) -+ if (ino->dentry->d_inode) { -+ struct dentry *parent = ino->dentry->d_parent; -+ if (atomic_dec_and_test(&ino->count)) { -+ p_ino = autofs4_dentry_ino(parent); -+ if (p_ino && parent != ino->dentry) -+ atomic_dec(&p_ino->count); -+ } - dput(ino->dentry); -+ } - ino->dentry = NULL; - } - if (ino->free) -@@ -76,26 +93,121 @@ void autofs4_free_ino(struct autofs_info - kfree(ino); - } - --static void autofs4_put_super(struct super_block *sb) -+/* -+ * Deal with the infamous "Busy inodes after umount ..." message. -+ * -+ * Clean up the dentry tree. This happens with autofs if the user -+ * space program goes away due to a SIGKILL, SIGSEGV etc. -+ */ -+static void autofs4_force_release(struct autofs_sb_info *sbi) -+{ -+ struct dentry *this_parent = sbi->sb->s_root; -+ struct list_head *next; -+ -+ if (!sbi->sb->s_root) -+ return; -+ -+ spin_lock(&dcache_lock); -+repeat: -+ next = this_parent->d_subdirs.next; -+resume: -+ while (next != &this_parent->d_subdirs) { -+ struct dentry *dentry = list_entry(next, struct dentry, d_child); -+ -+ /* Negative dentry - don`t care */ -+ if (!simple_positive(dentry)) { -+ next = next->next; -+ continue; -+ } -+ -+ if (!list_empty(&dentry->d_subdirs)) { -+ this_parent = dentry; -+ goto repeat; -+ } -+ -+ next = next->next; -+ spin_unlock(&dcache_lock); -+ -+ DPRINTK("dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ } -+ -+ if (this_parent != sbi->sb->s_root) { -+ struct dentry *dentry = this_parent; -+ -+ next = this_parent->d_child.next; -+ this_parent = this_parent->d_parent; -+ spin_unlock(&dcache_lock); -+ DPRINTK("parent dentry %p %.*s", -+ dentry, (int)dentry->d_name.len, dentry->d_name.name); -+ dput(dentry); -+ spin_lock(&dcache_lock); -+ goto resume; -+ } -+ spin_unlock(&dcache_lock); -+ shrink_dcache_sb(sbi->sb); -+} -+ -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs4_sbi(sb); - -- sb->s_fs_info = NULL; -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; - -- if ( !sbi->catatonic ) -- autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ -+ /* Free wait queues, close pipe */ -+ autofs4_catatonic_mode(sbi); - -+ /* Clean up and release dangling references */ -+ autofs4_force_release(sbi); -+ -+ sb->s_fs_info = NULL; - kfree(sbi); - -+out_kill_sb: - DPRINTK("shutting down"); -+ kill_anon_super(sb); -+} -+ -+static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) -+{ -+ struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb); -+ -+ if (!sbi) -+ return 0; -+ -+ seq_printf(m, ",fd=%d", sbi->pipefd); -+ 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); -+ seq_printf(m, ",maxproto=%d", sbi->max_proto); -+ -+ if (sbi->type & AUTOFS_TYPE_OFFSET) -+ seq_printf(m, ",offset"); -+ else if (sbi->type & AUTOFS_TYPE_DIRECT) -+ seq_printf(m, ",direct"); -+ else -+ seq_printf(m, ",indirect"); -+ -+ return 0; - } - - static struct super_operations autofs4_sops = { -- .put_super = autofs4_put_super, - .statfs = simple_statfs, -+ .show_options = autofs4_show_options, - }; - --enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, -+ Opt_indirect, Opt_direct, Opt_offset}; - - static match_table_t tokens = { - {Opt_fd, "fd=%u"}, -@@ -104,11 +216,15 @@ static match_table_t tokens = { - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, -+ {Opt_indirect, "indirect"}, -+ {Opt_direct, "direct"}, -+ {Opt_offset, "offset"}, - {Opt_err, NULL} - }; - - static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, -- pid_t *pgrp, int *minproto, int *maxproto) -+ pid_t *pgrp, unsigned int *type, -+ int *minproto, int *maxproto) - { - char *p; - substring_t args[MAX_OPT_ARGS]; -@@ -162,6 +278,15 @@ static int parse_options(char *options, - return 1; - *maxproto = option; - break; -+ case Opt_indirect: -+ *type = AUTOFS_TYPE_INDIRECT; -+ break; -+ case Opt_direct: -+ *type = AUTOFS_TYPE_DIRECT; -+ break; -+ case Opt_offset: -+ *type = AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +305,10 @@ static struct autofs_info *autofs4_mkroo - return ino; - } - -+static struct dentry_operations autofs4_sb_dentry_operations = { -+ .d_release = autofs4_dentry_release, -+}; -+ - int autofs4_fill_super(struct super_block *s, void *data, int silent) - { - struct inode * root_inode; -@@ -188,7 +317,6 @@ int autofs4_fill_super(struct super_bloc - int pipefd; - struct autofs_sb_info *sbi; - struct autofs_info *ino; -- int minproto, maxproto; - - sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); - if ( !sbi ) -@@ -199,14 +327,23 @@ int autofs4_fill_super(struct super_bloc - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipefd = -1; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; -+ sbi->type = AUTOFS_TYPE_INDIRECT; -+ sbi->min_proto = 0; -+ sbi->max_proto = 0; - init_MUTEX(&sbi->wq_sem); -+ spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; -+ spin_lock_init(&sbi->lookup_lock); -+ INIT_LIST_HEAD(&sbi->active_list); -+ INIT_LIST_HEAD(&sbi->expiring_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -219,38 +356,46 @@ int autofs4_fill_super(struct super_bloc - if (!ino) - goto fail_free; - root_inode = autofs4_get_inode(s, ino); -- kfree(ino); - if (!root_inode) -- goto fail_free; -+ goto fail_ino; - -- root_inode->i_op = &autofs4_root_inode_operations; -- root_inode->i_fop = &autofs4_root_operations; - root = d_alloc_root(root_inode); -- pipe = NULL; -- - if (!root) - goto fail_iput; -+ pipe = NULL; -+ -+ root->d_op = &autofs4_sb_dentry_operations; -+ root->d_fsdata = ino; - - /* Can this call block? */ - if (parse_options(data, &pipefd, - &root_inode->i_uid, &root_inode->i_gid, -- &sbi->oz_pgrp, -- &minproto, &maxproto)) { -+ &sbi->oz_pgrp, &sbi->type, -+ &sbi->min_proto, &sbi->max_proto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - -+ root_inode->i_fop = &autofs4_root_operations; -+ root_inode->i_op = sbi->type & AUTOFS_TYPE_TRIGGER ? -+ &autofs4_direct_root_inode_operations : -+ &autofs4_indirect_root_inode_operations; -+ - /* Couldn't this be tested earlier? */ -- if (maxproto < AUTOFS_MIN_PROTO_VERSION || -- minproto > AUTOFS_MAX_PROTO_VERSION) { -+ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || -+ sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", -- minproto, maxproto, -+ sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); - goto fail_dput; - } - -- sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto; -+ /* Establish highest kernel protocol version */ -+ if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) -+ sbi->version = AUTOFS_MAX_PROTO_VERSION; -+ else -+ sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; - - DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp); -@@ -263,6 +408,8 @@ int autofs4_fill_super(struct super_bloc - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->pipefd = pipefd; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -283,8 +430,11 @@ fail_dput: - fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -+fail_ino: -+ kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.9/fs/autofs4/waitq.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/waitq.c -+++ linux-2.6.9/fs/autofs4/waitq.c -@@ -3,7 +3,7 @@ - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved -- * Copyright 2001-2003 Ian Kent -+ * Copyright 2001-2006 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your -@@ -28,24 +28,31 @@ void autofs4_catatonic_mode(struct autof - { - struct autofs_wait_queue *wq, *nwq; - -+ down(&sbi->wq_sem); -+ if (sbi->catatonic) { -+ up(&sbi->wq_sem); -+ return; -+ } -+ - DPRINTK("entering catatonic mode"); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ -- while ( wq ) { -+ while (wq) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ -- kfree(wq->name); -- wq->name = NULL; -+ if (wq->name.name) { -+ kfree(wq->name.name); -+ wq->name.name = NULL; -+ } -+ wq->wait_ctr--; - wake_up_interruptible(&wq->queue); - wq = nwq; - } -- if (sbi->pipe) { -- fput(sbi->pipe); /* Close the pipe */ -- sbi->pipe = NULL; -- } -- -+ fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; -+ up(&sbi->wq_sem); - shrink_dcache_sb(sbi->sb); - } - -@@ -88,41 +95,90 @@ static void autofs4_notify_daemon(struct - struct autofs_wait_queue *wq, - int type) - { -- union autofs_packet_union pkt; -+ union { -+ struct autofs_packet_hdr hdr; -+ union autofs_packet_union v4_pkt; -+ union autofs_v5_packet_union v5_pkt; -+ } pkt; -+ struct file *pipe = NULL; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -- wq->wait_queue_token, wq->len, wq->name, type); -+ wq->wait_queue_token, wq->name.len, wq->name.name, type); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = sbi->version; - pkt.hdr.type = type; -- if (type == autofs_ptype_missing) { -- struct autofs_packet_missing *mp = &pkt.missing; -+ switch (type) { -+ /* Kernel protocol v4 missing and expire packets */ -+ case autofs_ptype_missing: -+ { -+ struct autofs_packet_missing *mp = &pkt.v4_pkt.missing; - - pktsz = sizeof(*mp); - - mp->wait_queue_token = wq->wait_queue_token; -- mp->len = wq->len; -- memcpy(mp->name, wq->name, wq->len); -- mp->name[wq->len] = '\0'; -- } else if (type == autofs_ptype_expire_multi) { -- struct autofs_packet_expire_multi *ep = &pkt.expire_multi; -+ mp->len = wq->name.len; -+ memcpy(mp->name, wq->name.name, wq->name.len); -+ mp->name[wq->name.len] = '\0'; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - - ep->wait_queue_token = wq->wait_queue_token; -- ep->len = wq->len; -- memcpy(ep->name, wq->name, wq->len); -- ep->name[wq->len] = '\0'; -- } else { -+ ep->len = wq->name.len; -+ memcpy(ep->name, wq->name.name, wq->name.len); -+ ep->name[wq->name.len] = '\0'; -+ break; -+ } -+ /* -+ * Kernel protocol v5 packet for handling indirect and direct -+ * mount missing and expire requests -+ */ -+ case autofs_ptype_missing_indirect: -+ case autofs_ptype_expire_indirect: -+ case autofs_ptype_missing_direct: -+ case autofs_ptype_expire_direct: -+ { -+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet; -+ -+ pktsz = sizeof(*packet); -+ -+ packet->wait_queue_token = wq->wait_queue_token; -+ packet->len = wq->name.len; -+ memcpy(packet->name, wq->name.name, wq->name.len); -+ packet->name[wq->name.len] = '\0'; -+ packet->dev = wq->dev; -+ packet->ino = wq->ino; -+ packet->uid = wq->uid; -+ packet->gid = wq->gid; -+ packet->pid = wq->pid; -+ packet->tgid = wq->tgid; -+ break; -+ } -+ default: - printk("autofs4_notify_daemon: bad type %d!\n", type); - return; - } - -- if (autofs4_write(sbi->pipe, &pkt, pktsz)) -- autofs4_catatonic_mode(sbi); -+ /* Check if we have become catatonic */ -+ down(&sbi->wq_sem); -+ if (!sbi->catatonic) { -+ pipe = sbi->pipe; -+ get_file(pipe); -+ } -+ up(&sbi->wq_sem); -+ -+ if (pipe) { -+ if (autofs4_write(pipe, &pkt, pktsz)) -+ autofs4_catatonic_mode(sbi); -+ fput(pipe); -+ } - } - - static int autofs4_getpath(struct autofs_sb_info *sbi, -@@ -138,7 +194,7 @@ static int autofs4_getpath(struct autofs - for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) - len += tmp->d_name.len + 1; - -- if (--len > NAME_MAX) { -+ if (!len || --len > NAME_MAX) { - spin_unlock(&dcache_lock); - return 0; - } -@@ -157,44 +213,170 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->name.hash == qstr->hash && -+ wq->name.len == qstr->len && -+ wq->name.name && !memcmp(wq->name, qstr->name, qstr->len)) -+ break; -+ } -+ return wq; -+} -+ -+/* -+ * Check if we have a valid request. -+ * Returns -+ * 1 if the request should continue. -+ * In this case we can return an autofs_wait_queue entry if one is -+ * found or NULL to idicate a new wait needs to be created. -+ * 0 or a negative errno if the request shouldn't continue. -+ */ -+static int validate_request(struct autofs_wait_queue **wait, -+ struct autofs_sb_info *sbi, -+ struct qstr *qstr, -+ struct dentry*dentry, enum autofs_notify notify) -+{ -+ struct autofs_wait_queue *wq; -+ struct autofs_info *ino; -+ -+ /* Wait in progress, continue; */ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ -+ *wait = NULL; -+ -+ /* If we don't yet have any info this is a new request */ -+ ino = autofs4_dentry_ino(dentry); -+ if (!ino) -+ return 1; -+ -+ /* -+ * If we've been asked to wait on an existing expire (NFY_NONE) -+ * but there is no wait in the queue ... -+ */ -+ if (notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the semaphore. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ schedule_timeout_interruptible(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) -+ return -EINTR; -+ -+ wq = autofs4_find_wait(sbi, qstr); -+ if (wq) { -+ *wait = wq; -+ return 1; -+ } -+ } -+ -+ /* -+ * Not ideal but the status has already gone. Of the two -+ * cases where we wait on NFY_NONE neither depend on the -+ * return status of the wait. -+ */ -+ return 0; -+ } -+ -+ /* -+ * If we've been asked to trigger a mount and the request -+ * completed while we waited on the semaphore ... -+ */ -+ if (notify == NFY_MOUNT) { -+ /* -+ * If the dentry isn't hashed just go ahead and try the -+ * mount again with a new wait (not much else we can do). -+ */ -+ if (!d_unhashed(dentry)) { -+ /* -+ * But if the dentry is hashed, that means that we -+ * got here through the revalidate path. Thus, we -+ * need to check if the dentry has been mounted -+ * while we waited on the wq_semaphore. If it has, -+ * simply return success. -+ */ -+ if (d_mountpoint(dentry)) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { - struct autofs_wait_queue *wq; -+ struct qstr qstr; - char *name; -- int len, status; -+ int status, ret, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - return -ENOENT; -- -+ -+ if (!dentry->d_inode) { -+ /* -+ * A wait for a negative dentry is invalid for certain -+ * cases. A direct or offset mount "always" has its mount -+ * point directory created and so the request dentry must -+ * be positive or the map key doesn't exist. The situation -+ * is very similar for indirect mounts except only dentrys -+ * in the root of the autofs file system may be negative. -+ */ -+ if (sbi->type & AUTOFS_TYPE_TRIGGER) -+ return -ENOENT; -+ else if (!IS_ROOT(dentry->d_parent)) -+ return -ENOENT; -+ } -+ - name = kmalloc(NAME_MAX + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - -- len = autofs4_getpath(sbi, dentry, &name); -- if (!len) { -- kfree(name); -- return -ENOENT; -+ /* If this is a direct mount request create a dummy name */ -+ if (IS_ROOT(dentry) && sbi->type & AUTOFS_TYPE_TRIGGER) -+ qstr.len = sprintf(name, "%p", dentry); -+ else { -+ qstr.len = autofs4_getpath(sbi, dentry, &name); -+ if (!qstr.len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ qstr.name = name; -+ qstr.hash = full_name_hash(name, qstr.len); - - if (down_interruptible(&sbi->wq_sem)) { -- kfree(name); -+ kfree(qstr.name); - return -EINTR; - } - -- for (wq = sbi->queues ; wq ; wq = wq->next) { -- if (wq->hash == dentry->d_name.hash && -- wq->len == len && -- wq->name && !memcmp(wq->name, name, len)) -- break; -+ ret = validate_request(&wq, sbi, &qstr, dentry, notify); -+ if (ret <= 0) { -+ if (ret == 0) -+ up(&sbi->wq_sem); -+ kfree(qstr.name); -+ return ret; - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -- kfree(name); -+ if (!wq) { -+ kfree(qstr.name); - up(&sbi->wq_sem); - return -ENOMEM; - } -@@ -205,41 +387,53 @@ int autofs4_wait(struct autofs_sb_info * - wq->next = sbi->queues; - sbi->queues = wq; - init_waitqueue_head(&wq->queue); -- wq->hash = dentry->d_name.hash; -- wq->name = name; -- wq->len = len; -+ memcpy(&wq->name, &qstr, sizeof(struct qstr)); -+ wq->dev = autofs4_get_dev(sbi); -+ wq->ino = autofs4_get_ino(sbi); -+ wq->uid = current->uid; -+ wq->gid = current->gid; -+ wq->pid = current->pid; -+ wq->tgid = current->tgid; - wq->status = -EINTR; /* Status return if interrupted */ -- atomic_set(&wq->wait_ctr, 2); -+ wq->wait_ctr = 2; - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -+ if (sbi->version < 5) { -+ if (notify == NFY_MOUNT) -+ type = autofs_ptype_missing; -+ else -+ type = autofs_ptype_expire_multi; -+ } else { -+ if (notify == NFY_MOUNT) -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_TRIGGER) ? -+ autofs_ptype_expire_direct : -+ autofs_ptype_expire_indirect; - } -+ -+ DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); -+ -+ /* autofs4_notify_daemon() may block */ -+ autofs4_notify_daemon(sbi, wq, type); - } else { -- atomic_inc(&wq->wait_ctr); -+ wq->wait_ctr++; - up(&sbi->wq_sem); -+ kfree(qstr.name); - DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); -- } -- -- /* wq->name is NULL if and only if the lock is already released */ -- -- if ( sbi->catatonic ) { -- /* We might have slept, so check again for catatonic mode */ -- wq->status = -ENOENT; -- if ( wq->name ) { -- kfree(wq->name); -- wq->name = NULL; -- } -+ (unsigned long) wq->wait_queue_token, wq->name.len, -+ wq->name.name, notify); - } - -- if ( wq->name ) { -+ /* -+ * wq->name.name is NULL iff the lock is already released -+ * or the mount has been made catatonic. -+ */ -+ if (wq->name.name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -250,7 +444,7 @@ int autofs4_wait(struct autofs_sb_info * - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); - -- wait_event_interruptible(wq->queue, wq->name == NULL); -+ wait_event_interruptible(wq->queue, wq->name.name == NULL); - - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; -@@ -263,8 +457,10 @@ int autofs4_wait(struct autofs_sb_info * - status = wq->status; - - /* Are we the last process to need status? */ -- if (atomic_dec_and_test(&wq->wait_ctr)) -+ down(&sbi->wq_sem); -+ if (!--wq->wait_ctr) - kfree(wq); -+ up(&sbi->wq_sem); - - return status; - } -@@ -275,27 +471,24 @@ int autofs4_wait_release(struct autofs_s - struct autofs_wait_queue *wq, **wql; - - down(&sbi->wq_sem); -- for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { -- if ( wq->wait_queue_token == wait_queue_token ) -+ for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) { -+ if (wq->wait_queue_token == wait_queue_token) - break; - } - -- if ( !wq ) { -+ if (!wq) { - up(&sbi->wq_sem); - return -EINVAL; - } - - *wql = wq->next; /* Unlink from chain */ -- up(&sbi->wq_sem); -- kfree(wq->name); -- wq->name = NULL; /* Do not wait on this queue */ -- -+ kfree(wq->name.name); -+ wq->name.name = NULL; /* Do not wait on this queue */ - wq->status = status; -- -- if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ -+ wake_up_interruptible(&wq->queue); -+ if (!--wq->wait_ctr) - kfree(wq); -- else -- wake_up_interruptible(&wq->queue); -+ up(&sbi->wq_sem); - - return 0; - } -Index: linux-2.6.9/include/linux/auto_fs4.h -=================================================================== ---- linux-2.6.9.orig/include/linux/auto_fs4.h -+++ linux-2.6.9/include/linux/auto_fs4.h -@@ -19,18 +19,42 @@ - #undef AUTOFS_MIN_PROTO_VERSION - #undef AUTOFS_MAX_PROTO_VERSION - --#define AUTOFS_PROTO_VERSION 4 -+#define AUTOFS_PROTO_VERSION 5 - #define AUTOFS_MIN_PROTO_VERSION 3 --#define AUTOFS_MAX_PROTO_VERSION 4 -+#define AUTOFS_MAX_PROTO_VERSION 5 - --#define AUTOFS_PROTO_SUBVERSION 5 -+#define AUTOFS_PROTO_SUBVERSION 0 - - /* Mask for expire behaviour */ - #define AUTOFS_EXP_IMMEDIATE 1 - #define AUTOFS_EXP_LEAVES 2 - --/* New message type */ --#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ -+#define AUTOFS_TYPE_ANY 0x0000 -+#define AUTOFS_TYPE_INDIRECT 0x0001 -+#define AUTOFS_TYPE_DIRECT 0x0002 -+#define AUTOFS_TYPE_OFFSET 0x0004 -+ -+/* Daemon notification packet types */ -+enum autofs_notify { -+ NFY_NONE, -+ NFY_MOUNT, -+ NFY_EXPIRE -+}; -+ -+/* Kernel protocol version 4 packet types */ -+ -+/* Expire entry (umount request) */ -+#define autofs_ptype_expire_multi 2 -+ -+/* Kernel protocol version 5 packet types */ -+ -+/* Indirect mount missing and expire requests. */ -+#define autofs_ptype_missing_indirect 3 -+#define autofs_ptype_expire_indirect 4 -+ -+/* Direct mount missing and expire requests */ -+#define autofs_ptype_missing_direct 5 -+#define autofs_ptype_expire_direct 6 - - /* v4 multi expire (via pipe) */ - struct autofs_packet_expire_multi { -@@ -47,10 +71,38 @@ union autofs_packet_union { - struct autofs_packet_expire_multi expire_multi; - }; - -+/* autofs v5 common packet struct */ -+struct autofs_v5_packet { -+ struct autofs_packet_hdr hdr; -+ autofs_wqt_t wait_queue_token; -+ __u32 dev; -+ __u64 ino; -+ __u32 uid; -+ __u32 gid; -+ __u32 pid; -+ __u32 tgid; -+ __u32 len; -+ char name[NAME_MAX+1]; -+}; -+ -+typedef struct autofs_v5_packet autofs_packet_missing_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_expire_indirect_t; -+typedef struct autofs_v5_packet autofs_packet_missing_direct_t; -+typedef struct autofs_v5_packet autofs_packet_expire_direct_t; -+ -+union autofs_v5_packet_union { -+ struct autofs_packet_hdr hdr; -+ struct autofs_v5_packet v5_packet; -+ autofs_packet_missing_indirect_t missing_indirect; -+ autofs_packet_expire_indirect_t expire_indirect; -+ autofs_packet_missing_direct_t missing_direct; -+ autofs_packet_expire_direct_t expire_direct; -+}; -+ - #define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) -+#define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI -+#define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI - #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) --#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) --#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) - #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) - - -Index: linux-2.6.9/fs/autofs/dirhash.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs/dirhash.c -+++ linux-2.6.9/fs/autofs/dirhash.c -@@ -92,7 +92,7 @@ struct autofs_dir_ent *autofs_expire(str - ; - dput(dentry); - -- if ( may_umount(mnt) == 0 ) { -+ if ( may_umount(mnt) ) { - mntput(mnt); - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ -Index: linux-2.6.9/fs/namespace.c -=================================================================== ---- linux-2.6.9.orig/fs/namespace.c -+++ linux-2.6.9/fs/namespace.c -@@ -309,9 +309,9 @@ resume: - spin_unlock(&vfsmount_lock); - - if (actual_refs > minimum_refs) -- return -EBUSY; -+ return 0; - -- return 0; -+ return 1; - } - - EXPORT_SYMBOL(may_umount_tree); -@@ -331,9 +331,10 @@ EXPORT_SYMBOL(may_umount_tree); - */ - int may_umount(struct vfsmount *mnt) - { -+ int ret = 1; - if (atomic_read(&mnt->mnt_count) > 2) -- return -EBUSY; -- return 0; -+ ret = 0; -+ return ret; - } - - EXPORT_SYMBOL(may_umount); -Index: linux-2.6.9/fs/namei.c -=================================================================== ---- linux-2.6.9.orig/fs/namei.c -+++ linux-2.6.9/fs/namei.c -@@ -288,6 +288,29 @@ void path_release_on_umount(struct namei - _mntput(nd->mnt); - } - -+static inline struct dentry *do_revalidate(struct dentry *dentry, struct nameidata *nd) -+{ -+ int status = dentry->d_op->d_revalidate(dentry, nd); -+ if (unlikely(status <= 0)) { -+ /* -+ * The dentry failed validation. -+ * If d_revalidate returned 0 attempt to invalidate -+ * the dentry otherwise d_revalidate is asking us -+ * to return a fail status. -+ */ -+ if (!status) { -+ if (!d_invalidate(dentry)) { -+ dput(dentry); -+ dentry = NULL; -+ } -+ } else { -+ dput(dentry); -+ dentry = ERR_PTR(status); -+ } -+ } -+ return dentry; -+} -+ - /* - * Internal lookup() using the new generic dcache. - * SMP-safe -@@ -302,12 +325,9 @@ static struct dentry * cached_lookup(str - if (!dentry) - dentry = d_lookup(parent, name); - -- if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { -- if (!dentry->d_op->d_revalidate(dentry, nd) && !d_invalidate(dentry)) { -- dput(dentry); -- dentry = NULL; -- } -- } -+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) -+ dentry = do_revalidate(dentry, nd); -+ - return dentry; - } - -@@ -400,10 +420,9 @@ static struct dentry * real_lookup(struc - */ - up(&dir->i_sem); - if (result->d_op && result->d_op->d_revalidate) { -- if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { -- dput(result); -+ result = do_revalidate(result, nd); -+ if (!result) - result = ERR_PTR(-ENOENT); -- } - } - return result; - } -@@ -635,12 +654,12 @@ need_lookup: - goto done; - - need_revalidate: -- if (dentry->d_op->d_revalidate(dentry, nd)) -- goto done; -- if (d_invalidate(dentry)) -- goto done; -- dput(dentry); -- goto need_lookup; -+ dentry = do_revalidate(dentry, nd); -+ if (!dentry) -+ goto need_lookup; -+ if (IS_ERR(dentry)) -+ goto fail; -+ goto done; - - fail: - return PTR_ERR(dentry); -@@ -746,6 +765,11 @@ int fastcall link_path_walk(const char * - - if (inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -@@ -800,6 +824,11 @@ last_component: - if ((lookup_flags & LOOKUP_FOLLOW) - && inode && inode->i_op && inode->i_op->follow_link) { - mntget(next.mnt); -+ if (next.mnt != nd->mnt) { -+ dput(nd->dentry); -+ nd->mnt = next.mnt; -+ nd->dentry = dget(next.dentry); -+ } - err = do_follow_link(next.dentry, nd); - dput(next.dentry); - mntput(next.mnt); -Index: linux-2.6.9/fs/autofs/init.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs/init.c -+++ linux-2.6.9/fs/autofs/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs_kill_sb, - }; - - static int __init init_autofs_fs(void) -Index: linux-2.6.9/fs/autofs/inode.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs/inode.c -+++ linux-2.6.9/fs/autofs/inode.c -@@ -19,11 +19,20 @@ - #include "autofs_i.h" - #include - --static void autofs_put_super(struct super_block *sb) -+void autofs4_kill_sb(struct super_block *sb) - { - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - -+ /* -+ * In the event of a failure in get_sb_nodev the superblock -+ * info is not present so nothing else has been setup, so -+ * just call kill_anon_super when we are called from -+ * deactivate_super. -+ */ -+ if (!sbi) -+ goto out_kill_sb; -+ - if ( !sbi->catatonic ) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -@@ -35,14 +44,15 @@ static void autofs_put_super(struct supe - - kfree(sb->s_fs_info); - -+out_kill_sb: - DPRINTK(("autofs: shutting down\n")); -+ kill_anon_super(sb); - } - - static void autofs_read_inode(struct inode *inode); - - static struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, -- .put_super = autofs_put_super, - .statfs = simple_statfs, - }; - -@@ -136,7 +146,8 @@ int autofs_fill_super(struct super_block - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; -- sbi->catatonic = 0; -+ sbi->pipe = NULL; -+ sbi->catatonic = 1; - sbi->exp_timeout = 0; - sbi->oz_pgrp = process_group(current); - autofs_initialize_hash(&sbi->dirhash); -@@ -178,6 +189,7 @@ int autofs_fill_super(struct super_block - if ( !pipe->f_op || !pipe->f_op->write ) - goto fail_fput; - sbi->pipe = pipe; -+ sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. -@@ -196,6 +208,7 @@ fail_iput: - iput(root_inode); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -Index: linux-2.6.9/fs/autofs/autofs_i.h -=================================================================== ---- linux-2.6.9.orig/fs/autofs/autofs_i.h -+++ linux-2.6.9/fs/autofs/autofs_i.h -@@ -150,6 +150,7 @@ extern struct file_operations autofs_roo - /* Initializing function */ - - int autofs_fill_super(struct super_block *, void *, int); -+void autofs_kill_sb(struct super_block *); - - /* Queue management functions */ - -Index: linux-2.6.9/fs/autofs4/init.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs4/init.c -+++ linux-2.6.9/fs/autofs4/init.c -@@ -24,7 +24,7 @@ static struct file_system_type autofs_fs - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, -- .kill_sb = kill_anon_super, -+ .kill_sb = autofs4_kill_sb, - }; - - static int __init init_autofs4_fs(void) -Index: linux-2.6.9/fs/autofs/waitq.c -=================================================================== ---- linux-2.6.9.orig/fs/autofs/waitq.c -+++ linux-2.6.9/fs/autofs/waitq.c -@@ -41,6 +41,7 @@ void autofs_catatonic_mode(struct autofs - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ -+ sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ - } - -Index: linux-2.6.9/include/linux/compat_ioctl.h -=================================================================== ---- linux-2.6.9.orig/include/linux/compat_ioctl.h -+++ linux-2.6.9/include/linux/compat_ioctl.h -@@ -552,8 +552,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) - COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) - COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) --COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) --COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) - COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) - /* DEVFS */ - COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)