diff --git a/CHANGELOG b/CHANGELOG index 5b0f265..671d336 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ - use /dev/urandom instead of /dev/random. - check for mtab pointing to /proc/mounts. - dynamically allocate interface config buffer. +- update kernel patches. 14/01/2008 autofs-5.0.3 ----------------------- diff --git a/patches/autofs4-2.6.10-v5-update-20080924.patch b/patches/autofs4-2.6.10-v5-update-20080924.patch new file mode 100644 index 0000000..b5d8ac5 --- /dev/null +++ b/patches/autofs4-2.6.10-v5-update-20080924.patch @@ -0,0 +1,3048 @@ +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#define AUTOFS_TYPE_INDIRECT 0x0001 ++#define AUTOFS_TYPE_DIRECT 0x0002 ++#define AUTOFS_TYPE_OFFSET 0x0004 ++ + 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 +143,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 +163,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 +175,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 +187,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 +231,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 *); +--- 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); +@@ -56,94 +56,152 @@ static int autofs4_check_mount(struct vf + mntget(mnt); + dget(dentry); + +- if (!follow_down(&mnt, &dentry)) ++ if (!autofs4_follow_mount(&mnt, &dentry)) + goto done; + +- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) +- ; +- + /* This is an autofs submount, we can't expire it */ + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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 +209,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; + } +@@ -213,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- linux-2.6.10.orig/include/linux/auto_fs4.h ++++ linux-2.6.10/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); + +--- 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! */ +--- 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); +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.10-v5-update.patch b/patches/autofs4-2.6.10-v5-update.patch deleted file mode 100644 index 91afc08..0000000 --- a/patches/autofs4-2.6.10-v5-update.patch +++ /dev/null @@ -1,2509 +0,0 @@ -diff -Nurp linux-2.6.10.orig/fs/autofs/autofs_i.h linux-2.6.10/fs/autofs/autofs_i.h ---- linux-2.6.10.orig/fs/autofs/autofs_i.h 2004-12-25 05:35:23.000000000 +0800 -+++ linux-2.6.10/fs/autofs/autofs_i.h 2008-01-14 12:44:12.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.10.orig/fs/autofs/dirhash.c linux-2.6.10/fs/autofs/dirhash.c ---- linux-2.6.10.orig/fs/autofs/dirhash.c 2004-12-25 05:33:51.000000000 +0800 -+++ linux-2.6.10/fs/autofs/dirhash.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.10.orig/fs/autofs/init.c linux-2.6.10/fs/autofs/init.c ---- linux-2.6.10.orig/fs/autofs/init.c 2004-12-25 05:35:01.000000000 +0800 -+++ linux-2.6.10/fs/autofs/init.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.10.orig/fs/autofs/inode.c linux-2.6.10/fs/autofs/inode.c ---- linux-2.6.10.orig/fs/autofs/inode.c 2004-12-25 05:35:23.000000000 +0800 -+++ linux-2.6.10/fs/autofs/inode.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.10.orig/fs/autofs/waitq.c linux-2.6.10/fs/autofs/waitq.c ---- linux-2.6.10.orig/fs/autofs/waitq.c 2004-12-25 05:35:50.000000000 +0800 -+++ linux-2.6.10/fs/autofs/waitq.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.10.orig/fs/autofs4/autofs_i.h linux-2.6.10/fs/autofs4/autofs_i.h ---- linux-2.6.10.orig/fs/autofs4/autofs_i.h 2004-12-25 05:35:01.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/autofs_i.h 2008-01-14 12:44:12.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,9 +75,15 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; -@@ -89,19 +91,30 @@ 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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -126,9 +139,18 @@ 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); -+ } - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ return pending; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -153,6 +175,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 +187,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 +231,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 *); -diff -Nurp linux-2.6.10.orig/fs/autofs4/expire.c linux-2.6.10/fs/autofs4/expire.c ---- linux-2.6.10.orig/fs/autofs4/expire.c 2004-12-25 05:35:14.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/expire.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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,19 +56,21 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!follow_down(&mnt, &dentry)) -+ if (!autofs4_follow_mount(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -- - /* This is an autofs submount, we can't expire it */ - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -76,74 +78,130 @@ done: - 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 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +281,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; -@@ -240,7 +308,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,33 +316,49 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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; -+ /* 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); -+ -+ /* Set this flag early to catch sys_chdir and the like */ -+ inf->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ expired = dentry; -+ break; - } -- /* Case 3: direct mount, expire individual leaves */ -+ spin_unlock(&sbi->fs_lock); -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -288,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -316,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -342,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.10.orig/fs/autofs4/init.c linux-2.6.10/fs/autofs4/init.c ---- linux-2.6.10.orig/fs/autofs4/init.c 2004-12-25 05:35:22.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/init.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.10.orig/fs/autofs4/inode.c linux-2.6.10/fs/autofs4/inode.c ---- linux-2.6.10.orig/fs/autofs4/inode.c 2004-12-25 05:35:23.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/inode.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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 - -@@ -46,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -65,10 +71,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 +91,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -+ /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -219,38 +353,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_DIRECT ? -+ &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 +405,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 +427,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; - } -diff -Nurp linux-2.6.10.orig/fs/autofs4/root.c linux-2.6.10/fs/autofs4/root.c ---- linux-2.6.10.orig/fs/autofs4/root.c 2004-12-25 05:33:49.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/root.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -205,24 +151,32 @@ static int autofs4_dir_open(struct inode - struct vfsmount *fp_mnt = mntget(mnt); - struct dentry *fp_dentry = dget(dentry); - -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -232,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -266,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -281,28 +237,34 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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); -- -- return 0; -+ -+ /* -+ * 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", -@@ -317,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -348,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -369,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -493,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -516,6 +692,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; - -@@ -540,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -562,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,12 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +789,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 +802,18 @@ 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); - __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 +827,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) ) -@@ -636,7 +841,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -645,6 +850,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; -@@ -728,7 +937,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.10.orig/fs/autofs4/waitq.c linux-2.6.10/fs/autofs4/waitq.c ---- linux-2.6.10.orig/fs/autofs4/waitq.c 2004-12-25 05:35:29.000000000 +0800 -+++ linux-2.6.10/fs/autofs4/waitq.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,43 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } -+ -+ /* -+ * 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. -+ */ -+ if (!wq) { -+ kfree(name); -+ up(&sbi->wq_sem); -+ return 0; -+ } - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -205,25 +291,44 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -+ 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); -+ - /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -- } -+ autofs4_notify_daemon(sbi, wq, type); - } 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); - } -@@ -275,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.10.orig/fs/namei.c linux-2.6.10/fs/namei.c ---- linux-2.6.10.orig/fs/namei.c 2004-12-25 05:34:30.000000000 +0800 -+++ linux-2.6.10/fs/namei.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.10.orig/fs/namespace.c linux-2.6.10/fs/namespace.c ---- linux-2.6.10.orig/fs/namespace.c 2004-12-25 05:35:01.000000000 +0800 -+++ linux-2.6.10/fs/namespace.c 2008-01-14 12:44:12.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.10.orig/include/linux/auto_fs4.h linux-2.6.10/include/linux/auto_fs4.h ---- linux-2.6.10.orig/include/linux/auto_fs4.h 2004-12-25 05:33:50.000000000 +0800 -+++ linux-2.6.10/include/linux/auto_fs4.h 2008-01-14 12:44:12.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.11-v5-update-20080924.patch b/patches/autofs4-2.6.11-v5-update-20080924.patch new file mode 100644 index 0000000..29566d2 --- /dev/null +++ b/patches/autofs4-2.6.11-v5-update-20080924.patch @@ -0,0 +1,3048 @@ +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#define AUTOFS_TYPE_INDIRECT 0x0001 ++#define AUTOFS_TYPE_DIRECT 0x0002 ++#define AUTOFS_TYPE_OFFSET 0x0004 ++ + 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 +143,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 +163,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 +175,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 +187,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 +231,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 *); +--- 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); +@@ -56,94 +56,152 @@ static int autofs4_check_mount(struct vf + mntget(mnt); + dget(dentry); + +- if (!follow_down(&mnt, &dentry)) ++ if (!autofs4_follow_mount(&mnt, &dentry)) + goto done; + +- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) +- ; +- + /* This is an autofs submount, we can't expire it */ + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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 +209,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; + } +@@ -213,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- linux-2.6.11.orig/include/linux/auto_fs4.h ++++ linux-2.6.11/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); + +--- 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! */ +--- 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); +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.11-v5-update.patch b/patches/autofs4-2.6.11-v5-update.patch deleted file mode 100644 index ffd0097..0000000 --- a/patches/autofs4-2.6.11-v5-update.patch +++ /dev/null @@ -1,2509 +0,0 @@ -diff -Nurp linux-2.6.11.orig/fs/autofs/autofs_i.h linux-2.6.11/fs/autofs/autofs_i.h ---- linux-2.6.11.orig/fs/autofs/autofs_i.h 2005-03-02 15:38:14.000000000 +0800 -+++ linux-2.6.11/fs/autofs/autofs_i.h 2008-01-14 12:45:40.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.11.orig/fs/autofs/dirhash.c linux-2.6.11/fs/autofs/dirhash.c ---- linux-2.6.11.orig/fs/autofs/dirhash.c 2005-03-02 15:37:48.000000000 +0800 -+++ linux-2.6.11/fs/autofs/dirhash.c 2008-01-14 12:45:39.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.11.orig/fs/autofs/init.c linux-2.6.11/fs/autofs/init.c ---- linux-2.6.11.orig/fs/autofs/init.c 2005-03-02 15:38:13.000000000 +0800 -+++ linux-2.6.11/fs/autofs/init.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.11.orig/fs/autofs/inode.c linux-2.6.11/fs/autofs/inode.c ---- linux-2.6.11.orig/fs/autofs/inode.c 2005-03-02 15:38:38.000000000 +0800 -+++ linux-2.6.11/fs/autofs/inode.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.11.orig/fs/autofs/waitq.c linux-2.6.11/fs/autofs/waitq.c ---- linux-2.6.11.orig/fs/autofs/waitq.c 2005-03-02 15:38:37.000000000 +0800 -+++ linux-2.6.11/fs/autofs/waitq.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.11.orig/fs/autofs4/autofs_i.h linux-2.6.11/fs/autofs4/autofs_i.h ---- linux-2.6.11.orig/fs/autofs4/autofs_i.h 2005-03-02 15:38:13.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/autofs_i.h 2008-01-14 12:45:40.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,9 +75,15 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; -@@ -89,19 +91,30 @@ 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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -126,9 +139,18 @@ 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); -+ } - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ return pending; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -153,6 +175,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 +187,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 +231,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 *); -diff -Nurp linux-2.6.11.orig/fs/autofs4/expire.c linux-2.6.11/fs/autofs4/expire.c ---- linux-2.6.11.orig/fs/autofs4/expire.c 2005-03-02 15:38:13.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/expire.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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,19 +56,21 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!follow_down(&mnt, &dentry)) -+ if (!autofs4_follow_mount(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -- - /* This is an autofs submount, we can't expire it */ - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -76,74 +78,130 @@ done: - 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 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +281,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; -@@ -240,7 +308,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,33 +316,49 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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; -+ /* 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); -+ -+ /* Set this flag early to catch sys_chdir and the like */ -+ inf->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ expired = dentry; -+ break; - } -- /* Case 3: direct mount, expire individual leaves */ -+ spin_unlock(&sbi->fs_lock); -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -288,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -316,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -342,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.11.orig/fs/autofs4/init.c linux-2.6.11/fs/autofs4/init.c ---- linux-2.6.11.orig/fs/autofs4/init.c 2005-03-02 15:38:13.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/init.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.11.orig/fs/autofs4/inode.c linux-2.6.11/fs/autofs4/inode.c ---- linux-2.6.11.orig/fs/autofs4/inode.c 2005-03-02 15:38:17.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/inode.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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 - -@@ -46,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -65,10 +71,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 +91,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -+ /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -220,38 +354,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_DIRECT ? -+ &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 +406,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 +428,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; - } -diff -Nurp linux-2.6.11.orig/fs/autofs4/root.c linux-2.6.11/fs/autofs4/root.c ---- linux-2.6.11.orig/fs/autofs4/root.c 2005-03-02 15:37:31.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/root.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -205,24 +151,32 @@ static int autofs4_dir_open(struct inode - struct vfsmount *fp_mnt = mntget(mnt); - struct dentry *fp_dentry = dget(dentry); - -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -232,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -266,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -281,28 +237,34 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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); -- -- return 0; -+ -+ /* -+ * 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", -@@ -317,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -348,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -369,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -493,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -516,6 +692,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; - -@@ -540,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -562,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,12 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +789,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 +802,18 @@ 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); - __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 +827,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) ) -@@ -636,7 +841,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -645,6 +850,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; -@@ -728,7 +937,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.11.orig/fs/autofs4/waitq.c linux-2.6.11/fs/autofs4/waitq.c ---- linux-2.6.11.orig/fs/autofs4/waitq.c 2005-03-02 15:38:26.000000000 +0800 -+++ linux-2.6.11/fs/autofs4/waitq.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,43 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } -+ -+ /* -+ * 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. -+ */ -+ if (!wq) { -+ kfree(name); -+ up(&sbi->wq_sem); -+ return 0; -+ } - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -205,25 +291,44 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -+ 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); -+ - /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -- } -+ autofs4_notify_daemon(sbi, wq, type); - } 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); - } -@@ -275,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.11.orig/fs/namei.c linux-2.6.11/fs/namei.c ---- linux-2.6.11.orig/fs/namei.c 2005-03-02 15:37:55.000000000 +0800 -+++ linux-2.6.11/fs/namei.c 2008-01-14 12:45:40.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.11.orig/fs/namespace.c linux-2.6.11/fs/namespace.c ---- linux-2.6.11.orig/fs/namespace.c 2005-03-02 15:38:13.000000000 +0800 -+++ linux-2.6.11/fs/namespace.c 2008-01-14 12:45:39.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.11.orig/include/linux/auto_fs4.h linux-2.6.11/include/linux/auto_fs4.h ---- linux-2.6.11.orig/include/linux/auto_fs4.h 2005-03-02 15:37:47.000000000 +0800 -+++ linux-2.6.11/include/linux/auto_fs4.h 2008-01-14 12:45:40.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.12-v5-update-20080924.patch b/patches/autofs4-2.6.12-v5-update-20080924.patch new file mode 100644 index 0000000..e4c5c19 --- /dev/null +++ b/patches/autofs4-2.6.12-v5-update-20080924.patch @@ -0,0 +1,3067 @@ +--- 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); + +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#define AUTOFS_TYPE_INDIRECT 0x0001 ++#define AUTOFS_TYPE_DIRECT 0x0002 ++#define AUTOFS_TYPE_OFFSET 0x0004 ++ + 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 +142,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 +163,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 +175,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 +187,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 +231,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 *); +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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); +@@ -56,98 +56,152 @@ static int autofs4_check_mount(struct vf + mntget(mnt); + dget(dentry); + +- if (!follow_down(&mnt, &dentry)) ++ if (!autofs4_follow_mount(&mnt, &dentry)) + goto done; + +- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) +- ; +- + /* This is an autofs submount, we can't expire it */ + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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, +@@ -155,58 +209,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; + } +@@ -217,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- linux-2.6.12.orig/include/linux/auto_fs4.h ++++ linux-2.6.12/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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! */ +--- 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); +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.12-v5-update.patch b/patches/autofs4-2.6.12-v5-update.patch deleted file mode 100644 index 5e8623a..0000000 --- a/patches/autofs4-2.6.12-v5-update.patch +++ /dev/null @@ -1,2495 +0,0 @@ -diff -Nurp linux-2.6.12.orig/fs/autofs/autofs_i.h linux-2.6.12/fs/autofs/autofs_i.h ---- linux-2.6.12.orig/fs/autofs/autofs_i.h 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs/autofs_i.h 2008-01-14 12:47:28.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.12.orig/fs/autofs/dirhash.c linux-2.6.12/fs/autofs/dirhash.c ---- linux-2.6.12.orig/fs/autofs/dirhash.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs/dirhash.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.12.orig/fs/autofs/init.c linux-2.6.12/fs/autofs/init.c ---- linux-2.6.12.orig/fs/autofs/init.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs/init.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.12.orig/fs/autofs/inode.c linux-2.6.12/fs/autofs/inode.c ---- linux-2.6.12.orig/fs/autofs/inode.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs/inode.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.12.orig/fs/autofs/waitq.c linux-2.6.12/fs/autofs/waitq.c ---- linux-2.6.12.orig/fs/autofs/waitq.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs/waitq.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.12.orig/fs/autofs4/autofs_i.h linux-2.6.12/fs/autofs4/autofs_i.h ---- linux-2.6.12.orig/fs/autofs4/autofs_i.h 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/autofs_i.h 2008-01-14 12:47:28.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,31 +75,46 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; - }; - - #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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -164,6 +175,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 +187,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 +231,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 *); -diff -Nurp linux-2.6.12.orig/fs/autofs4/expire.c linux-2.6.12/fs/autofs4/expire.c ---- linux-2.6.12.orig/fs/autofs4/expire.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/expire.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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,19 +56,21 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!follow_down(&mnt, &dentry)) -+ if (!autofs4_follow_mount(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -- - /* This is an autofs submount, we can't expire it */ - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -76,78 +78,130 @@ done: - 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, -@@ -155,58 +209,68 @@ 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 (!sbi->exp_timeout || !root) -+ return NULL; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -217,10 +281,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; -@@ -244,7 +308,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,31 +316,36 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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)) { -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - - /* Set this flag early to catch sys_chdir and the like */ -@@ -286,7 +355,10 @@ static struct dentry *autofs4_expire(str - break; - } - spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -300,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -328,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -354,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.12.orig/fs/autofs4/init.c linux-2.6.12/fs/autofs4/init.c ---- linux-2.6.12.orig/fs/autofs4/init.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/init.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.12.orig/fs/autofs4/inode.c linux-2.6.12/fs/autofs4/inode.c ---- linux-2.6.12.orig/fs/autofs4/inode.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/inode.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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 - -@@ -46,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -65,10 +71,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 +91,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -+ /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -221,38 +354,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_DIRECT ? -+ &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 +406,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 +428,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; - } -diff -Nurp linux-2.6.12.orig/fs/autofs4/root.c linux-2.6.12/fs/autofs4/root.c ---- linux-2.6.12.orig/fs/autofs4/root.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/root.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -205,24 +151,32 @@ static int autofs4_dir_open(struct inode - struct vfsmount *fp_mnt = mntget(mnt); - struct dentry *fp_dentry = dget(dentry); - -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -232,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -266,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -281,28 +237,34 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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); -- -- return 0; -+ -+ /* -+ * 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", -@@ -317,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -348,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -369,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -438,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -493,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -516,6 +692,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; - -@@ -540,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -549,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -562,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -596,7 +791,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 +804,20 @@ 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(&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 +831,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) ) -@@ -638,7 +845,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -647,6 +854,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; -@@ -730,7 +941,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.12.orig/fs/autofs4/waitq.c linux-2.6.12/fs/autofs4/waitq.c ---- linux-2.6.12.orig/fs/autofs4/waitq.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/autofs4/waitq.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,43 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } -+ -+ /* -+ * 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. -+ */ -+ if (!wq) { -+ kfree(name); -+ up(&sbi->wq_sem); -+ return 0; -+ } - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -205,13 +291,40 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); -+ -+ 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); -+ -+ /* autofs4_notify_daemon() may block */ -+ autofs4_notify_daemon(sbi, wq, type); - } else { - atomic_inc(&wq->wait_ctr); - up(&sbi->wq_sem); -@@ -220,17 +333,6 @@ int autofs4_wait(struct autofs_sb_info * - (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); -- -- DPRINTK(("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", -- (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify)); -- -- /* autofs4_notify_daemon() may block */ -- autofs4_notify_daemon(sbi, wq, type); -- } -- - /* wq->name is NULL if and only if the lock is already released */ - - if ( sbi->catatonic ) { -@@ -278,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.12.orig/fs/namei.c linux-2.6.12/fs/namei.c ---- linux-2.6.12.orig/fs/namei.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/namei.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.12.orig/fs/namespace.c linux-2.6.12/fs/namespace.c ---- linux-2.6.12.orig/fs/namespace.c 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/fs/namespace.c 2008-01-14 12:47:28.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.12.orig/include/linux/auto_fs4.h linux-2.6.12/include/linux/auto_fs4.h ---- linux-2.6.12.orig/include/linux/auto_fs4.h 2005-06-18 03:48:29.000000000 +0800 -+++ linux-2.6.12/include/linux/auto_fs4.h 2008-01-14 12:47:28.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.13-v5-update-20080924.patch b/patches/autofs4-2.6.13-v5-update-20080924.patch new file mode 100644 index 0000000..5db837b --- /dev/null +++ b/patches/autofs4-2.6.13-v5-update-20080924.patch @@ -0,0 +1,3043 @@ +--- 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); + +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#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; + 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 +142,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 +163,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 +175,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 +187,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 +204,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 +231,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 *); +--- 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); +@@ -63,88 +63,145 @@ static int autofs4_check_mount(struct vf + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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, +@@ -152,58 +209,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; + } +@@ -214,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- 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! */ +--- 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); +--- linux-2.6.13.orig/include/linux/auto_fs4.h ++++ linux-2.6.13/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.13-v5-update.patch b/patches/autofs4-2.6.13-v5-update.patch deleted file mode 100644 index c0e76f5..0000000 --- a/patches/autofs4-2.6.13-v5-update.patch +++ /dev/null @@ -1,2453 +0,0 @@ -diff -Nurp linux-2.6.13.orig/fs/autofs/autofs_i.h linux-2.6.13/fs/autofs/autofs_i.h ---- linux-2.6.13.orig/fs/autofs/autofs_i.h 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs/autofs_i.h 2008-01-14 12:48:44.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.13.orig/fs/autofs/dirhash.c linux-2.6.13/fs/autofs/dirhash.c ---- linux-2.6.13.orig/fs/autofs/dirhash.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs/dirhash.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.13.orig/fs/autofs/init.c linux-2.6.13/fs/autofs/init.c ---- linux-2.6.13.orig/fs/autofs/init.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs/init.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.13.orig/fs/autofs/inode.c linux-2.6.13/fs/autofs/inode.c ---- linux-2.6.13.orig/fs/autofs/inode.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs/inode.c 2008-01-14 12:48:45.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.13.orig/fs/autofs/waitq.c linux-2.6.13/fs/autofs/waitq.c ---- linux-2.6.13.orig/fs/autofs/waitq.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs/waitq.c 2008-01-14 12:48:45.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.13.orig/fs/autofs4/autofs_i.h linux-2.6.13/fs/autofs4/autofs_i.h ---- linux-2.6.13.orig/fs/autofs4/autofs_i.h 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/autofs_i.h 2008-01-14 12:48:45.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,32 +75,46 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; - }; - - #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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -165,6 +175,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 +187,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 +204,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 +231,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 *); -diff -Nurp linux-2.6.13.orig/fs/autofs4/expire.c linux-2.6.13/fs/autofs4/expire.c ---- linux-2.6.13.orig/fs/autofs4/expire.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/expire.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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); -@@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vf - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -73,78 +78,130 @@ done: - 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, -@@ -152,58 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +281,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; -@@ -241,7 +308,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,31 +316,36 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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)) { -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - - /* Set this flag early to catch sys_chdir and the like */ -@@ -283,7 +355,10 @@ static struct dentry *autofs4_expire(str - break; - } - spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -297,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -325,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -351,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.13.orig/fs/autofs4/init.c linux-2.6.13/fs/autofs4/init.c ---- linux-2.6.13.orig/fs/autofs4/init.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/init.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.13.orig/fs/autofs4/inode.c linux-2.6.13/fs/autofs4/inode.c ---- linux-2.6.13.orig/fs/autofs4/inode.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/inode.c 2008-01-14 12:48:45.000000000 +0900 -@@ -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 -@@ -47,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -66,10 +71,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 +99,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 +133,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 +146,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -245,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -287,38 +354,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_DIRECT ? -+ &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 +406,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 +428,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; - } -diff -Nurp linux-2.6.13.orig/fs/autofs4/root.c linux-2.6.13/fs/autofs4/root.c ---- linux-2.6.13.orig/fs/autofs4/root.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/root.c 2008-01-14 12:48:45.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -208,25 +154,29 @@ static int autofs4_dir_open(struct inode - if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { - dput(fp_dentry); - mntput(fp_mnt); -- return -ENOENT; -+ 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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -236,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -270,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -285,27 +237,26 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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 -@@ -313,7 +264,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return 0; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -328,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -359,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -504,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -527,6 +692,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; - -@@ -551,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -573,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +791,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 +804,20 @@ 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(&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 +831,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,7 +845,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +854,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; -@@ -741,7 +941,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.13.orig/fs/autofs4/waitq.c linux-2.6.13/fs/autofs4/waitq.c ---- linux-2.6.13.orig/fs/autofs4/waitq.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/autofs4/waitq.c 2008-01-14 12:48:45.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,50 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -- } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } - -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -+ /* -+ * 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. -+ */ -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); -- return -ENOENT; -+ return 0; - } -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -212,30 +291,46 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } 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); - } - - /* wq->name is NULL if and only if the lock is already released */ -@@ -285,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.13.orig/fs/namei.c linux-2.6.13/fs/namei.c ---- linux-2.6.13.orig/fs/namei.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/namei.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.13.orig/fs/namespace.c linux-2.6.13/fs/namespace.c ---- linux-2.6.13.orig/fs/namespace.c 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/fs/namespace.c 2008-01-14 12:48:44.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.13.orig/include/linux/auto_fs4.h linux-2.6.13/include/linux/auto_fs4.h ---- linux-2.6.13.orig/include/linux/auto_fs4.h 2005-08-29 07:41:01.000000000 +0800 -+++ linux-2.6.13/include/linux/auto_fs4.h 2008-01-14 12:48:45.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.14-v5-update-20080924.patch b/patches/autofs4-2.6.14-v5-update-20080924.patch new file mode 100644 index 0000000..55d0ff9 --- /dev/null +++ b/patches/autofs4-2.6.14-v5-update-20080924.patch @@ -0,0 +1,3085 @@ +--- 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); + +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#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; + 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 +142,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 +163,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 +175,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 +187,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 +204,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 +231,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 *); +--- 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); +@@ -63,88 +63,145 @@ static int autofs4_check_mount(struct vf + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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, +@@ -152,58 +209,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; + } +@@ -214,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- 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! */ +--- 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); +--- linux-2.6.14.orig/include/linux/auto_fs4.h ++++ linux-2.6.14/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.14-v5-update.patch b/patches/autofs4-2.6.14-v5-update.patch deleted file mode 100644 index bd4871c..0000000 --- a/patches/autofs4-2.6.14-v5-update.patch +++ /dev/null @@ -1,2495 +0,0 @@ -diff -Nurp linux-2.6.14.orig/fs/autofs/autofs_i.h linux-2.6.14/fs/autofs/autofs_i.h ---- linux-2.6.14.orig/fs/autofs/autofs_i.h 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs/autofs_i.h 2008-01-14 12:49:59.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.14.orig/fs/autofs/dirhash.c linux-2.6.14/fs/autofs/dirhash.c ---- linux-2.6.14.orig/fs/autofs/dirhash.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs/dirhash.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.14.orig/fs/autofs/init.c linux-2.6.14/fs/autofs/init.c ---- linux-2.6.14.orig/fs/autofs/init.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs/init.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.14.orig/fs/autofs/inode.c linux-2.6.14/fs/autofs/inode.c ---- linux-2.6.14.orig/fs/autofs/inode.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs/inode.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.14.orig/fs/autofs/waitq.c linux-2.6.14/fs/autofs/waitq.c ---- linux-2.6.14.orig/fs/autofs/waitq.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs/waitq.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.14.orig/fs/autofs4/autofs_i.h linux-2.6.14/fs/autofs4/autofs_i.h ---- linux-2.6.14.orig/fs/autofs4/autofs_i.h 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/autofs_i.h 2008-01-14 12:49:59.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,32 +75,46 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; - }; - - #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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -165,6 +175,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 +187,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 +204,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 +231,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 *); -diff -Nurp linux-2.6.14.orig/fs/autofs4/expire.c linux-2.6.14/fs/autofs4/expire.c ---- linux-2.6.14.orig/fs/autofs4/expire.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/expire.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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); -@@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vf - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -73,78 +78,130 @@ done: - 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, -@@ -152,58 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +281,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; -@@ -241,7 +308,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,31 +316,36 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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)) { -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - - /* Set this flag early to catch sys_chdir and the like */ -@@ -283,7 +355,10 @@ static struct dentry *autofs4_expire(str - break; - } - spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -297,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -325,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -351,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.14.orig/fs/autofs4/init.c linux-2.6.14/fs/autofs4/init.c ---- linux-2.6.14.orig/fs/autofs4/init.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/init.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.14.orig/fs/autofs4/inode.c linux-2.6.14/fs/autofs4/inode.c ---- linux-2.6.14.orig/fs/autofs4/inode.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/inode.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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 -@@ -47,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -66,10 +71,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 +99,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 +133,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 +146,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -245,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -287,38 +354,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_DIRECT ? -+ &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 +406,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 +428,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; - } -diff -Nurp linux-2.6.14.orig/fs/autofs4/root.c linux-2.6.14/fs/autofs4/root.c ---- linux-2.6.14.orig/fs/autofs4/root.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/root.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -208,25 +154,29 @@ static int autofs4_dir_open(struct inode - if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { - dput(fp_dentry); - mntput(fp_mnt); -- return -ENOENT; -+ 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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -236,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -270,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -285,27 +237,26 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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 -@@ -313,7 +264,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return 0; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -328,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -359,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -504,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -527,6 +692,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; - -@@ -551,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -573,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +791,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 +804,20 @@ 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(&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 +831,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,7 +845,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +854,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; -@@ -741,7 +941,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.14.orig/fs/autofs4/waitq.c linux-2.6.14/fs/autofs4/waitq.c ---- linux-2.6.14.orig/fs/autofs4/waitq.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/autofs4/waitq.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,50 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -- } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } - -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -+ /* -+ * 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. -+ */ -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); -- return -ENOENT; -+ return 0; - } -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -212,30 +291,46 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } 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); - } - - /* wq->name is NULL if and only if the lock is already released */ -@@ -285,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.14.orig/fs/namei.c linux-2.6.14/fs/namei.c ---- linux-2.6.14.orig/fs/namei.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/namei.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.14.orig/fs/namespace.c linux-2.6.14/fs/namespace.c ---- linux-2.6.14.orig/fs/namespace.c 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/fs/namespace.c 2008-01-14 12:49:59.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.14.orig/include/linux/auto_fs4.h linux-2.6.14/include/linux/auto_fs4.h ---- linux-2.6.14.orig/include/linux/auto_fs4.h 2005-10-28 08:02:08.000000000 +0800 -+++ linux-2.6.14/include/linux/auto_fs4.h 2008-01-14 12:49:59.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.15-v5-update-20080924.patch b/patches/autofs4-2.6.15-v5-update-20080924.patch new file mode 100644 index 0000000..dd87a6d --- /dev/null +++ b/patches/autofs4-2.6.15-v5-update-20080924.patch @@ -0,0 +1,3096 @@ +--- 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); + +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#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; + 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 +142,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 +163,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 +175,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 +187,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 +204,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 +231,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 *); +--- 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); +@@ -63,88 +63,145 @@ static int autofs4_check_mount(struct vf + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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, +@@ -152,58 +209,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; + } +@@ -214,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- 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! */ +--- 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; + } +--- linux-2.6.15.orig/include/linux/auto_fs4.h ++++ linux-2.6.15/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.15-v5-update.patch b/patches/autofs4-2.6.15-v5-update.patch deleted file mode 100644 index 325c4fd..0000000 --- a/patches/autofs4-2.6.15-v5-update.patch +++ /dev/null @@ -1,2509 +0,0 @@ -diff -Nurp linux-2.6.15.orig/fs/autofs/autofs_i.h linux-2.6.15/fs/autofs/autofs_i.h ---- linux-2.6.15.orig/fs/autofs/autofs_i.h 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs/autofs_i.h 2008-01-14 12:51:31.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.15.orig/fs/autofs/dirhash.c linux-2.6.15/fs/autofs/dirhash.c ---- linux-2.6.15.orig/fs/autofs/dirhash.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs/dirhash.c 2008-01-14 12:51:30.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.15.orig/fs/autofs/init.c linux-2.6.15/fs/autofs/init.c ---- linux-2.6.15.orig/fs/autofs/init.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs/init.c 2008-01-14 12:51:31.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.15.orig/fs/autofs/inode.c linux-2.6.15/fs/autofs/inode.c ---- linux-2.6.15.orig/fs/autofs/inode.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs/inode.c 2008-01-14 12:51:32.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.15.orig/fs/autofs/waitq.c linux-2.6.15/fs/autofs/waitq.c ---- linux-2.6.15.orig/fs/autofs/waitq.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs/waitq.c 2008-01-14 12:51:31.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.15.orig/fs/autofs4/autofs_i.h linux-2.6.15/fs/autofs4/autofs_i.h ---- linux-2.6.15.orig/fs/autofs4/autofs_i.h 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/autofs_i.h 2008-01-14 12:51:32.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,32 +75,46 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; - }; - - #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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -165,6 +175,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 +187,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 +204,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 +231,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 *); -diff -Nurp linux-2.6.15.orig/fs/autofs4/expire.c linux-2.6.15/fs/autofs4/expire.c ---- linux-2.6.15.orig/fs/autofs4/expire.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/expire.c 2008-01-14 12:51:31.000000000 +0900 -@@ -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); -@@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vf - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -73,78 +78,130 @@ done: - 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, -@@ -152,58 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +281,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; -@@ -241,7 +308,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,31 +316,36 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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)) { -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - - /* Set this flag early to catch sys_chdir and the like */ -@@ -283,7 +355,10 @@ static struct dentry *autofs4_expire(str - break; - } - spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -297,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -325,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -351,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.15.orig/fs/autofs4/init.c linux-2.6.15/fs/autofs4/init.c ---- linux-2.6.15.orig/fs/autofs4/init.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/init.c 2008-01-14 12:51:31.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.15.orig/fs/autofs4/inode.c linux-2.6.15/fs/autofs4/inode.c ---- linux-2.6.15.orig/fs/autofs4/inode.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/inode.c 2008-01-14 12:51:32.000000000 +0900 -@@ -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 -@@ -45,7 +47,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -64,10 +69,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 +97,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 +131,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 +144,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +212,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 +274,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -243,6 +301,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 +313,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 +323,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -285,38 +352,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_DIRECT ? -+ &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 +404,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 +426,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; - } -diff -Nurp linux-2.6.15.orig/fs/autofs4/root.c linux-2.6.15/fs/autofs4/root.c ---- linux-2.6.15.orig/fs/autofs4/root.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/root.c 2008-01-14 12:51:32.000000000 +0900 -@@ -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,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,7 +31,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -46,7 +48,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +56,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, -@@ -81,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -168,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -178,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -194,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -208,25 +154,29 @@ static int autofs4_dir_open(struct inode - if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { - dput(fp_dentry); - mntput(fp_mnt); -- return -ENOENT; -+ 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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -236,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -270,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -285,27 +237,26 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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 -@@ -313,7 +264,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return 0; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -328,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -359,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -380,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -449,43 +493,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -504,19 +643,45 @@ 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 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 dentry; - - return NULL; - } -@@ -527,6 +692,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; - -@@ -551,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -560,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -573,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -607,7 +791,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 +804,20 @@ 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(&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 +831,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,7 +845,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -658,6 +854,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; -@@ -741,7 +941,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.15.orig/fs/autofs4/waitq.c linux-2.6.15/fs/autofs4/waitq.c ---- linux-2.6.15.orig/fs/autofs4/waitq.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/autofs4/waitq.c 2008-01-14 12:51:32.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,50 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -- } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } - -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -+ /* -+ * 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. -+ */ -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); -- return -ENOENT; -+ return 0; - } -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -212,42 +291,58 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } 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); - } - - /* wq->name is NULL if and only if the lock is already released */ - -- if ( sbi->catatonic ) { -+ 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 ) { -+ if (wq->name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -283,12 +378,12 @@ 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; - } -diff -Nurp linux-2.6.15.orig/fs/namei.c linux-2.6.15/fs/namei.c ---- linux-2.6.15.orig/fs/namei.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/namei.c 2008-01-14 12:51:31.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.15.orig/fs/namespace.c linux-2.6.15/fs/namespace.c ---- linux-2.6.15.orig/fs/namespace.c 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/fs/namespace.c 2008-01-14 12:51:30.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.15.orig/include/linux/auto_fs4.h linux-2.6.15/include/linux/auto_fs4.h ---- linux-2.6.15.orig/include/linux/auto_fs4.h 2006-01-03 11:21:10.000000000 +0800 -+++ linux-2.6.15/include/linux/auto_fs4.h 2008-01-14 12:51:32.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.16-v5-update-20080924.patch b/patches/autofs4-2.6.16-v5-update-20080924.patch new file mode 100644 index 0000000..0b7d2d0 --- /dev/null +++ b/patches/autofs4-2.6.16-v5-update-20080924.patch @@ -0,0 +1,3109 @@ +--- 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); + +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#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; + 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 +142,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 +163,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 +175,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 +187,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 +204,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 +231,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 *); +--- 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); +@@ -63,88 +63,145 @@ static int autofs4_check_mount(struct vf + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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); +- +- 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_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 +209,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 (this_parent != parent) { +- next = this_parent->d_u.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; + } +@@ -214,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- 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! */ +--- 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; + } +--- linux-2.6.16.orig/include/linux/auto_fs4.h ++++ linux-2.6.16/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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.16-v5-update.patch b/patches/autofs4-2.6.16-v5-update.patch deleted file mode 100644 index 8f03d05..0000000 --- a/patches/autofs4-2.6.16-v5-update.patch +++ /dev/null @@ -1,2525 +0,0 @@ -diff -Nurp linux-2.6.16.orig/fs/autofs/autofs_i.h linux-2.6.16/fs/autofs/autofs_i.h ---- linux-2.6.16.orig/fs/autofs/autofs_i.h 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs/autofs_i.h 2008-01-14 12:53:06.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.16.orig/fs/autofs/dirhash.c linux-2.6.16/fs/autofs/dirhash.c ---- linux-2.6.16.orig/fs/autofs/dirhash.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs/dirhash.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.16.orig/fs/autofs/init.c linux-2.6.16/fs/autofs/init.c ---- linux-2.6.16.orig/fs/autofs/init.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs/init.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.16.orig/fs/autofs/inode.c linux-2.6.16/fs/autofs/inode.c ---- linux-2.6.16.orig/fs/autofs/inode.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs/inode.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.16.orig/fs/autofs/waitq.c linux-2.6.16/fs/autofs/waitq.c ---- linux-2.6.16.orig/fs/autofs/waitq.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs/waitq.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.16.orig/fs/autofs4/autofs_i.h linux-2.6.16/fs/autofs4/autofs_i.h ---- linux-2.6.16.orig/fs/autofs4/autofs_i.h 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/autofs_i.h 2008-01-14 12:53:06.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,32 +75,46 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; - }; - - #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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -165,6 +175,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 +187,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 +204,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 +231,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 *); -diff -Nurp linux-2.6.16.orig/fs/autofs4/expire.c linux-2.6.16/fs/autofs4/expire.c ---- linux-2.6.16.orig/fs/autofs4/expire.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/expire.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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); -@@ -63,9 +63,14 @@ static int autofs4_check_mount(struct vf - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -73,78 +78,130 @@ done: - 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); -- -- 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_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 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_u.d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -214,10 +281,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; -@@ -241,7 +308,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,31 +316,36 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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)) { -+ if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { - struct autofs_info *inf = autofs4_dentry_ino(dentry); - - /* Set this flag early to catch sys_chdir and the like */ -@@ -283,7 +355,10 @@ static struct dentry *autofs4_expire(str - break; - } - spin_unlock(&sbi->fs_lock); -- /* Case 3: direct mount, expire individual leaves */ -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -297,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -325,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -351,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.16.orig/fs/autofs4/init.c linux-2.6.16/fs/autofs4/init.c ---- linux-2.6.16.orig/fs/autofs4/init.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/init.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.16.orig/fs/autofs4/inode.c linux-2.6.16/fs/autofs4/inode.c ---- linux-2.6.16.orig/fs/autofs4/inode.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/inode.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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 -@@ -45,7 +47,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -64,10 +69,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 +97,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 +131,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 +144,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +212,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 +274,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -243,6 +301,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 +313,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 +323,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -285,38 +352,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_DIRECT ? -+ &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 +404,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 +426,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; - } -diff -Nurp linux-2.6.16.orig/fs/autofs4/root.c linux-2.6.16/fs/autofs4/root.c ---- linux-2.6.16.orig/fs/autofs4/root.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/root.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -30,7 +32,7 @@ static int autofs4_dir_close(struct inod - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -47,7 +49,7 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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 +57,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, -@@ -82,87 +92,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -170,8 +100,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -180,12 +118,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -195,13 +136,15 @@ static int autofs4_dir_open(struct inode - if (!empty) - d_invalidate(dentry); - -- nd.dentry = dentry; -- nd.mnt = mnt; - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -212,25 +155,29 @@ static int autofs4_dir_open(struct inode - if (!autofs4_follow_mount(&fp_mnt, &fp_dentry)) { - dput(fp_dentry); - mntput(fp_mnt); -- return -ENOENT; -+ 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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -240,26 +187,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -274,7 +223,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -289,27 +238,26 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return dcache_readdir(file, dirent, filldir); - } - --static int try_to_fill_dentry(struct vfsmount *mnt, struct dentry *dentry, int flags) -+static int try_to_fill_dentry(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); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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 -@@ -317,38 +265,36 @@ static int try_to_fill_dentry(struct vfs - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return 0; -+ return -EAGAIN; - } - - 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -363,19 +309,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return NULL; -+ -+out_error: -+ path_release(nd); -+ return ERR_PTR(status); - } - - /* -@@ -384,47 +394,72 @@ 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 */ - if (autofs4_ispending(dentry)) { -- 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; -+ -+ /* -+ * 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; - } - - /* 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 +469,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -453,43 +497,138 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -508,19 +647,45 @@ 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 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 dentry; - - return NULL; - } -@@ -531,6 +696,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; - -@@ -555,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -564,6 +730,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -577,9 +747,10 @@ 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 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. - * - * 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 +761,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 +779,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -611,7 +795,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 +808,20 @@ 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(&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 +835,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) ) -@@ -653,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -662,6 +858,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; -@@ -745,7 +945,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.16.orig/fs/autofs4/waitq.c linux-2.6.16/fs/autofs4/waitq.c ---- linux-2.6.16.orig/fs/autofs4/waitq.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/autofs4/waitq.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,50 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -- } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } - -- if ( !wq ) { -- /* Can't wait for an expire if there's no mount */ -- if (notify == NFY_NONE && !d_mountpoint(dentry)) { -+ /* -+ * 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. -+ */ -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); -- return -ENOENT; -+ return 0; - } -+ } - -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -212,42 +291,58 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); - - /* autofs4_notify_daemon() may block */ - autofs4_notify_daemon(sbi, wq, type); -+ } 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); - } - - /* wq->name is NULL if and only if the lock is already released */ - -- if ( sbi->catatonic ) { -+ 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 ) { -+ if (wq->name) { - /* Block all but "shutdown" signals while waiting */ - sigset_t oldset; - unsigned long irqflags; -@@ -283,12 +378,12 @@ 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; - } -diff -Nurp linux-2.6.16.orig/fs/namei.c linux-2.6.16/fs/namei.c ---- linux-2.6.16.orig/fs/namei.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/namei.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.16.orig/fs/namespace.c linux-2.6.16/fs/namespace.c ---- linux-2.6.16.orig/fs/namespace.c 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/fs/namespace.c 2008-01-14 12:53:06.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.16.orig/include/linux/auto_fs4.h linux-2.6.16/include/linux/auto_fs4.h ---- linux-2.6.16.orig/include/linux/auto_fs4.h 2006-03-20 13:53:29.000000000 +0800 -+++ linux-2.6.16/include/linux/auto_fs4.h 2008-01-14 12:53:06.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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) diff --git a/patches/autofs4-2.6.17-v5-update-20080924.patch b/patches/autofs4-2.6.17-v5-update-20080924.patch new file mode 100644 index 0000000..2ad0886 --- /dev/null +++ b/patches/autofs4-2.6.17-v5-update-20080924.patch @@ -0,0 +1,2079 @@ +--- 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); + +--- linux-2.6.17.orig/fs/autofs4/expire.c ++++ linux-2.6.17/fs/autofs4/expire.c +@@ -73,8 +73,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 +174,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +439,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 +457,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 +@@ -430,9 +490,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.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); +--- 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) +--- 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; + } +--- 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 */ + +--- linux-2.6.17.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.17/fs/autofs4/autofs_i.h +@@ -54,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; +@@ -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,7 +89,7 @@ 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 +@@ -96,7 +100,6 @@ struct autofs_wait_queue { + + struct autofs_sb_info { + u32 magic; +- struct dentry *root; + int pipefd; + struct file *pipe; + pid_t oz_pgrp; +@@ -113,6 +116,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 +143,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 +164,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 +234,4 @@ out: + } + + void autofs4_dentry_release(struct dentry *); +- ++extern void autofs4_kill_sb(struct super_block *); +--- 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) +--- 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, + }; +@@ -314,9 +327,9 @@ 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; +@@ -328,6 +341,9 @@ int autofs4_fill_super(struct super_bloc + 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; +@@ -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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -318,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; + } +--- 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 */ + } + +--- linux-2.6.17.orig/include/linux/auto_fs4.h ++++ linux-2.6.17/include/linux/auto_fs4.h +@@ -59,6 +59,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 +85,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.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.17-v5-update.patch b/patches/autofs4-2.6.17-v5-update.patch deleted file mode 100644 index 2abebf7..0000000 --- a/patches/autofs4-2.6.17-v5-update.patch +++ /dev/null @@ -1,876 +0,0 @@ -diff -Nurp linux-2.6.17.orig/fs/autofs/autofs_i.h linux-2.6.17/fs/autofs/autofs_i.h ---- linux-2.6.17.orig/fs/autofs/autofs_i.h 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs/autofs_i.h 2008-01-14 12:54:39.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.17.orig/fs/autofs/init.c linux-2.6.17/fs/autofs/init.c ---- linux-2.6.17.orig/fs/autofs/init.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs/init.c 2008-01-14 12:54:39.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.17.orig/fs/autofs/inode.c linux-2.6.17/fs/autofs/inode.c ---- linux-2.6.17.orig/fs/autofs/inode.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs/inode.c 2008-01-14 12:54:39.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.17.orig/fs/autofs/waitq.c linux-2.6.17/fs/autofs/waitq.c ---- linux-2.6.17.orig/fs/autofs/waitq.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs/waitq.c 2008-01-14 12:54:39.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.17.orig/fs/autofs4/autofs_i.h linux-2.6.17/fs/autofs4/autofs_i.h ---- linux-2.6.17.orig/fs/autofs4/autofs_i.h 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/autofs_i.h 2008-01-14 12:54:39.000000000 +0900 -@@ -54,6 +54,8 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -96,7 +98,6 @@ struct autofs_wait_queue { - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; - int pipefd; - struct file *pipe; - pid_t oz_pgrp; -@@ -113,6 +114,8 @@ 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; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -231,4 +234,4 @@ out: - } - - void autofs4_dentry_release(struct dentry *); -- -+extern void autofs4_kill_sb(struct super_block *); -diff -Nurp linux-2.6.17.orig/fs/autofs4/expire.c linux-2.6.17/fs/autofs4/expire.c ---- linux-2.6.17.orig/fs/autofs4/expire.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/expire.c 2008-01-14 12:54:39.000000000 +0900 -@@ -174,6 +174,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; -diff -Nurp linux-2.6.17.orig/fs/autofs4/init.c linux-2.6.17/fs/autofs4/init.c ---- linux-2.6.17.orig/fs/autofs4/init.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/init.c 2008-01-14 12:54:39.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.17.orig/fs/autofs4/inode.c linux-2.6.17/fs/autofs4/inode.c ---- linux-2.6.17.orig/fs/autofs4/inode.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/inode.c 2008-01-14 12:54:39.000000000 +0900 -@@ -47,6 +47,8 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; - atomic_set(&ino->count, 0); - -@@ -95,9 +97,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 +131,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 +144,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +198,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, - }; -@@ -314,9 +323,9 @@ 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; -@@ -328,6 +337,8 @@ int autofs4_fill_super(struct super_bloc - 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); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -394,13 +405,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 +430,7 @@ fail_ino: - kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -diff -Nurp linux-2.6.17.orig/fs/autofs4/root.c linux-2.6.17/fs/autofs4/root.c ---- linux-2.6.17.orig/fs/autofs4/root.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/root.c 2008-01-14 12:54:39.000000000 +0900 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -137,7 +139,9 @@ static int autofs4_dir_open(struct inode - nd.flags = LOOKUP_DIRECTORY; - ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!ret) { -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; - dcache_dir_close(inode, file); - goto out; - } -@@ -261,7 +265,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return -ENOENT; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -279,9 +283,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,8 +294,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -337,7 +337,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -357,7 +357,7 @@ static void *autofs4_follow_link(struct - * don't try to mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); -@@ -400,13 +400,32 @@ 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 */ - if (autofs4_ispending(dentry)) { -- 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; -+ -+ /* -+ * 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; - } - - /* Negative dentry.. invalidate if "old" */ -@@ -421,9 +440,19 @@ 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 +469,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -459,10 +497,80 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -478,25 +586,49 @@ 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_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -515,19 +647,45 @@ 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 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 dentry; - - return NULL; - } -@@ -563,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -589,9 +747,10 @@ 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 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. - * - * 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 +779,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -631,6 +797,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 +808,9 @@ 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(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -677,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -diff -Nurp linux-2.6.17.orig/fs/autofs4/waitq.c linux-2.6.17/fs/autofs4/waitq.c ---- linux-2.6.17.orig/fs/autofs4/waitq.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/autofs4/waitq.c 2008-01-14 12:54:39.000000000 +0900 -@@ -41,10 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -87,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -101,7 +103,7 @@ 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); - -@@ -113,7 +115,7 @@ static void autofs4_notify_daemon(struct - } - 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); - -@@ -132,7 +134,7 @@ 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); - -diff -Nurp linux-2.6.17.orig/fs/namei.c linux-2.6.17/fs/namei.c ---- linux-2.6.17.orig/fs/namei.c 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/fs/namei.c 2008-01-14 12:54:39.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.17.orig/include/linux/auto_fs4.h linux-2.6.17/include/linux/auto_fs4.h ---- linux-2.6.17.orig/include/linux/auto_fs4.h 2006-06-18 09:49:35.000000000 +0800 -+++ linux-2.6.17/include/linux/auto_fs4.h 2008-01-14 12:54:39.000000000 +0900 -@@ -59,6 +59,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,12 +85,13 @@ 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) diff --git a/patches/autofs4-2.6.18-v5-update-20080924.patch b/patches/autofs4-2.6.18-v5-update-20080924.patch new file mode 100644 index 0000000..7bcc1ee --- /dev/null +++ b/patches/autofs4-2.6.18-v5-update-20080924.patch @@ -0,0 +1,2064 @@ +--- 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); + +--- 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 +@@ -54,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; +@@ -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,7 +89,7 @@ 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 +@@ -96,7 +100,6 @@ struct autofs_wait_queue { + + struct autofs_sb_info { + u32 magic; +- struct dentry *root; + int pipefd; + struct file *pipe; + pid_t oz_pgrp; +@@ -113,6 +116,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 +143,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 +164,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 +234,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,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) +--- 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, + }; +@@ -314,9 +327,9 @@ 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; +@@ -328,6 +341,9 @@ int autofs4_fill_super(struct super_bloc + 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; +@@ -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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -318,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; + } +--- 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 +@@ -59,6 +59,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 +85,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 +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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) diff --git a/patches/autofs4-2.6.18-v5-update.patch b/patches/autofs4-2.6.18-v5-update.patch deleted file mode 100644 index 09ba97f..0000000 --- a/patches/autofs4-2.6.18-v5-update.patch +++ /dev/null @@ -1,860 +0,0 @@ -diff -Nurp linux-2.6.18.orig/fs/autofs/autofs_i.h linux-2.6.18/fs/autofs/autofs_i.h ---- linux-2.6.18.orig/fs/autofs/autofs_i.h 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs/autofs_i.h 2008-01-14 12:56:12.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.18.orig/fs/autofs/init.c linux-2.6.18/fs/autofs/init.c ---- linux-2.6.18.orig/fs/autofs/init.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs/init.c 2008-01-14 12:56:12.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.18.orig/fs/autofs/inode.c linux-2.6.18/fs/autofs/inode.c ---- linux-2.6.18.orig/fs/autofs/inode.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs/inode.c 2008-01-14 12:56:12.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.18.orig/fs/autofs/waitq.c linux-2.6.18/fs/autofs/waitq.c ---- linux-2.6.18.orig/fs/autofs/waitq.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs/waitq.c 2008-01-14 12:56:12.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.18.orig/fs/autofs4/autofs_i.h linux-2.6.18/fs/autofs4/autofs_i.h ---- linux-2.6.18.orig/fs/autofs4/autofs_i.h 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs4/autofs_i.h 2008-01-14 12:56:12.000000000 +0900 -@@ -54,6 +54,8 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -96,7 +98,6 @@ struct autofs_wait_queue { - - struct autofs_sb_info { - u32 magic; -- struct dentry *root; - int pipefd; - struct file *pipe; - pid_t oz_pgrp; -@@ -113,6 +114,8 @@ 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; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -231,4 +234,4 @@ out: - } - - void autofs4_dentry_release(struct dentry *); -- -+extern void autofs4_kill_sb(struct super_block *); -diff -Nurp linux-2.6.18.orig/fs/autofs4/init.c linux-2.6.18/fs/autofs4/init.c ---- linux-2.6.18.orig/fs/autofs4/init.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs4/init.c 2008-01-14 12:56:12.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.18.orig/fs/autofs4/inode.c linux-2.6.18/fs/autofs4/inode.c ---- linux-2.6.18.orig/fs/autofs4/inode.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs4/inode.c 2008-01-14 12:56:12.000000000 +0900 -@@ -47,6 +47,8 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; - atomic_set(&ino->count, 0); - -@@ -95,9 +97,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 +131,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 +144,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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 +198,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, - }; -@@ -314,9 +323,9 @@ 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; -@@ -328,6 +337,8 @@ int autofs4_fill_super(struct super_bloc - 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); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -394,13 +405,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 +430,7 @@ fail_ino: - kfree(ino); - fail_free: - kfree(sbi); -+ s->s_fs_info = NULL; - fail_unlock: - return -EINVAL; - } -diff -Nurp linux-2.6.18.orig/fs/autofs4/root.c linux-2.6.18/fs/autofs4/root.c ---- linux-2.6.18.orig/fs/autofs4/root.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs4/root.c 2008-01-14 12:56:12.000000000 +0900 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -137,7 +139,9 @@ static int autofs4_dir_open(struct inode - nd.flags = LOOKUP_DIRECTORY; - ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!ret) { -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; - dcache_dir_close(inode, file); - goto out; - } -@@ -261,7 +265,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return -ENOENT; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -279,9 +283,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,8 +294,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -337,7 +337,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -357,7 +357,7 @@ static void *autofs4_follow_link(struct - * don't try to mount it again. - */ - spin_lock(&dcache_lock); -- if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { -+ if (!d_mountpoint(dentry) && __simple_empty(dentry)) { - spin_unlock(&dcache_lock); - - status = try_to_fill_dentry(dentry, 0); -@@ -400,13 +400,32 @@ 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 */ - if (autofs4_ispending(dentry)) { -- 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; -+ -+ /* -+ * 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; - } - - /* Negative dentry.. invalidate if "old" */ -@@ -421,9 +440,19 @@ 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 +469,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -459,10 +497,80 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -478,25 +586,49 @@ 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_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -515,19 +647,45 @@ 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 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 dentry; - - return NULL; - } -@@ -563,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -589,9 +747,10 @@ 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 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. - * - * 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 +779,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -631,6 +797,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 +808,9 @@ 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(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -677,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -diff -Nurp linux-2.6.18.orig/fs/autofs4/waitq.c linux-2.6.18/fs/autofs4/waitq.c ---- linux-2.6.18.orig/fs/autofs4/waitq.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/autofs4/waitq.c 2008-01-14 12:56:12.000000000 +0900 -@@ -41,10 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -87,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -101,7 +103,7 @@ 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); - -@@ -113,7 +115,7 @@ static void autofs4_notify_daemon(struct - } - 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); - -@@ -132,7 +134,7 @@ 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); - -diff -Nurp linux-2.6.18.orig/fs/namei.c linux-2.6.18/fs/namei.c ---- linux-2.6.18.orig/fs/namei.c 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/fs/namei.c 2008-01-14 12:56:12.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.18.orig/include/linux/auto_fs4.h linux-2.6.18/include/linux/auto_fs4.h ---- linux-2.6.18.orig/include/linux/auto_fs4.h 2006-09-20 11:42:06.000000000 +0800 -+++ linux-2.6.18/include/linux/auto_fs4.h 2008-01-14 12:56:12.000000000 +0900 -@@ -59,6 +59,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,12 +85,13 @@ 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) diff --git a/patches/autofs4-2.6.19-v5-update-20080924.patch b/patches/autofs4-2.6.19-v5-update-20080924.patch new file mode 100644 index 0000000..e177cee --- /dev/null +++ b/patches/autofs4-2.6.19-v5-update-20080924.patch @@ -0,0 +1,1790 @@ +--- 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,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); + } +@@ -334,6 +341,9 @@ int autofs4_fill_super(struct super_bloc + 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; +@@ -426,7 +436,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,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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -315,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.19.orig/include/linux/auto_fs4.h ++++ linux-2.6.19/include/linux/auto_fs4.h +@@ -59,6 +59,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 +85,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 +@@ -52,6 +52,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 +71,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,7 +87,7 @@ 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 +@@ -110,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) +@@ -134,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) +@@ -159,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 *); +--- 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); + +--- linux-2.6.19.orig/fs/autofs4/expire.c ++++ linux-2.6.19/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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) diff --git a/patches/autofs4-2.6.19-v5-update.patch b/patches/autofs4-2.6.19-v5-update.patch deleted file mode 100644 index 421be5f..0000000 --- a/patches/autofs4-2.6.19-v5-update.patch +++ /dev/null @@ -1,523 +0,0 @@ -diff -Nurp linux-2.6.19.orig/fs/autofs/inode.c linux-2.6.19/fs/autofs/inode.c ---- linux-2.6.19.orig/fs/autofs/inode.c 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/fs/autofs/inode.c 2008-01-14 12:57:43.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.19.orig/fs/autofs4/autofs_i.h linux-2.6.19/fs/autofs4/autofs_i.h ---- linux-2.6.19.orig/fs/autofs4/autofs_i.h 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/fs/autofs4/autofs_i.h 2008-01-14 12:57:43.000000000 +0900 -@@ -52,6 +52,8 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -110,6 +112,8 @@ 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; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -diff -Nurp linux-2.6.19.orig/fs/autofs4/inode.c linux-2.6.19/fs/autofs4/inode.c ---- linux-2.6.19.orig/fs/autofs4/inode.c 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/fs/autofs4/inode.c 2008-01-14 12:57:43.000000000 +0900 -@@ -48,6 +48,8 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; - atomic_set(&ino->count, 0); - -@@ -152,21 +154,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; -+ goto out_kill_sb; - -- sb->s_fs_info = NULL; -- -- if ( !sbi->catatonic ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* 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); - } -@@ -334,6 +337,8 @@ int autofs4_fill_super(struct super_bloc - 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); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -426,7 +431,6 @@ fail_ino: - fail_free: - kfree(sbi); - s->s_fs_info = NULL; -- kill_anon_super(s); - fail_unlock: - return -EINVAL; - } -diff -Nurp linux-2.6.19.orig/fs/autofs4/root.c linux-2.6.19/fs/autofs4/root.c ---- linux-2.6.19.orig/fs/autofs4/root.c 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/fs/autofs4/root.c 2008-01-14 12:57:43.000000000 +0900 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -263,7 +265,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return -ENOENT; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -292,8 +294,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -336,7 +337,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -413,7 +414,16 @@ static int autofs4_revalidate(struct den - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) -- return 1; -+ 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; - } -@@ -459,6 +469,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -478,10 +497,80 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -497,25 +586,49 @@ 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_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -534,6 +647,8 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -@@ -544,12 +659,33 @@ static struct dentry *autofs4_lookup(str - - /* - * 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 dentry; - - return NULL; - } -@@ -585,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -611,9 +747,10 @@ 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 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. - * - * 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 +779,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -653,6 +797,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 +808,9 @@ 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(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -699,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -diff -Nurp linux-2.6.19.orig/fs/autofs4/waitq.c linux-2.6.19/fs/autofs4/waitq.c ---- linux-2.6.19.orig/fs/autofs4/waitq.c 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/fs/autofs4/waitq.c 2008-01-14 12:57:43.000000000 +0900 -@@ -84,7 +84,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,7 +102,7 @@ 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); - -@@ -110,7 +114,7 @@ static void autofs4_notify_daemon(struct - } - 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); - -@@ -129,7 +133,7 @@ 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); - -diff -Nurp linux-2.6.19.orig/include/linux/auto_fs4.h linux-2.6.19/include/linux/auto_fs4.h ---- linux-2.6.19.orig/include/linux/auto_fs4.h 2006-11-30 05:57:37.000000000 +0800 -+++ linux-2.6.19/include/linux/auto_fs4.h 2008-01-14 12:57:43.000000000 +0900 -@@ -59,6 +59,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,12 +85,13 @@ 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) diff --git a/patches/autofs4-2.6.20-v5-update-20080924.patch b/patches/autofs4-2.6.20-v5-update-20080924.patch new file mode 100644 index 0000000..380d62d --- /dev/null +++ b/patches/autofs4-2.6.20-v5-update-20080924.patch @@ -0,0 +1,1740 @@ +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -315,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.20.orig/include/linux/auto_fs4.h ++++ linux-2.6.20/include/linux/auto_fs4.h +@@ -59,6 +59,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 +85,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 +@@ -52,6 +52,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 +71,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,7 +87,7 @@ 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 +@@ -110,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) +@@ -134,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) +@@ -160,6 +163,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 *); +--- 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: +@@ -336,6 +341,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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); + +--- linux-2.6.20.orig/fs/autofs4/expire.c ++++ linux-2.6.20/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.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.patch b/patches/autofs4-2.6.20-v5-update.patch deleted file mode 100644 index 0e86129..0000000 --- a/patches/autofs4-2.6.20-v5-update.patch +++ /dev/null @@ -1,471 +0,0 @@ -diff -Nurp linux-2.6.20.orig/fs/autofs4/autofs_i.h linux-2.6.20/fs/autofs4/autofs_i.h ---- linux-2.6.20.orig/fs/autofs4/autofs_i.h 2007-02-05 03:44:54.000000000 +0900 -+++ linux-2.6.20/fs/autofs4/autofs_i.h 2008-01-14 12:59:13.000000000 +0900 -@@ -52,6 +52,8 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; - atomic_t count; -@@ -110,6 +112,8 @@ 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; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -diff -Nurp linux-2.6.20.orig/fs/autofs4/inode.c linux-2.6.20/fs/autofs4/inode.c ---- linux-2.6.20.orig/fs/autofs4/inode.c 2007-02-05 03:44:54.000000000 +0900 -+++ linux-2.6.20/fs/autofs4/inode.c 2008-01-14 12:59:13.000000000 +0900 -@@ -48,6 +48,8 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; - atomic_set(&ino->count, 0); - -@@ -158,14 +160,13 @@ void autofs4_kill_sb(struct super_block - if (!sbi) - goto out_kill_sb; - -- sb->s_fs_info = NULL; -- -- if ( !sbi->catatonic ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - /* Clean up and release dangling references */ - autofs4_force_release(sbi); - -+ sb->s_fs_info = NULL; - kfree(sbi); - - out_kill_sb: -@@ -336,6 +337,8 @@ int autofs4_fill_super(struct super_bloc - 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); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -diff -Nurp linux-2.6.20.orig/fs/autofs4/root.c linux-2.6.20/fs/autofs4/root.c ---- linux-2.6.20.orig/fs/autofs4/root.c 2007-02-05 03:44:54.000000000 +0900 -+++ linux-2.6.20/fs/autofs4/root.c 2008-01-14 12:59:14.000000000 +0900 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -263,7 +265,7 @@ static int try_to_fill_dentry(struct den - */ - status = d_invalidate(dentry); - if (status != -EBUSY) -- return -ENOENT; -+ return -EAGAIN; - } - - DPRINTK("dentry=%p %.*s ino=%p", -@@ -292,8 +294,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -336,7 +337,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -413,7 +414,16 @@ static int autofs4_revalidate(struct den - */ - status = try_to_fill_dentry(dentry, flags); - if (status == 0) -- return 1; -+ 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; - } -@@ -459,6 +469,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -478,10 +497,80 @@ 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) -+{ -+ 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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *unhashed; - int oz_mode; - - DPRINTK("name = %.*s", -@@ -497,25 +586,49 @@ 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_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -534,6 +647,8 @@ static struct dentry *autofs4_lookup(str - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { -+ if (unhashed) -+ dput(unhashed); - return ERR_PTR(-ERESTARTNOINTR); - } - } -@@ -544,12 +659,33 @@ static struct dentry *autofs4_lookup(str - - /* - * 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 dentry; - - return NULL; - } -@@ -585,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -611,9 +747,10 @@ 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 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. - * - * 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 +779,14 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ spin_lock(&dentry->d_lock); -+ __d_drop(dentry); -+ spin_unlock(&dentry->d_lock); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -653,6 +797,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 +808,9 @@ 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(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -@@ -699,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -diff -Nurp linux-2.6.20.orig/fs/autofs4/waitq.c linux-2.6.20/fs/autofs4/waitq.c ---- linux-2.6.20.orig/fs/autofs4/waitq.c 2007-02-05 03:44:54.000000000 +0900 -+++ linux-2.6.20/fs/autofs4/waitq.c 2008-01-14 12:59:13.000000000 +0900 -@@ -84,7 +84,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,7 +102,7 @@ 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); - -@@ -110,7 +114,7 @@ static void autofs4_notify_daemon(struct - } - 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); - -@@ -129,7 +133,7 @@ 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); - -diff -Nurp linux-2.6.20.orig/include/linux/auto_fs4.h linux-2.6.20/include/linux/auto_fs4.h ---- linux-2.6.20.orig/include/linux/auto_fs4.h 2007-02-05 03:44:54.000000000 +0900 -+++ linux-2.6.20/include/linux/auto_fs4.h 2008-01-14 12:59:13.000000000 +0900 -@@ -59,6 +59,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,12 +85,13 @@ 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) diff --git a/patches/autofs4-2.6.21-v5-update.patch b/patches/autofs4-2.6.21-v5-update.patch deleted file mode 100644 index 9e0d679..0000000 --- a/patches/autofs4-2.6.21-v5-update.patch +++ /dev/null @@ -1,108 +0,0 @@ -diff -Nurp linux-2.6.21.orig/fs/autofs4/root.c linux-2.6.21/fs/autofs4/root.c ---- linux-2.6.21.orig/fs/autofs4/root.c 2007-04-26 11:08:32.000000000 +0800 -+++ linux-2.6.21/fs/autofs4/root.c 2008-01-14 13:01:03.000000000 +0900 -@@ -20,6 +20,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -292,8 +294,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -336,7 +337,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -588,19 +589,20 @@ static struct dentry *autofs4_lookup(str - 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. -+ * 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); -+ d_instantiate(dentry, NULL); - } else { - struct autofs_info *ino = autofs4_dentry_ino(unhashed); - DPRINTK("rehash %p with %p", dentry, unhashed); -@@ -608,15 +610,17 @@ static struct dentry *autofs4_lookup(str - * 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. - */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -+ 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"); - } -- d_rehash(unhashed); - dentry = unhashed; - } - -@@ -659,7 +663,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. -@@ -717,7 +721,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -845,7 +849,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; diff --git a/patches/autofs4-2.6.22-v5-update-20080924.patch b/patches/autofs4-2.6.22-v5-update-20080924.patch new file mode 100644 index 0000000..fe78e98 --- /dev/null +++ b/patches/autofs4-2.6.22-v5-update-20080924.patch @@ -0,0 +1,1682 @@ +--- 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); + +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.22.orig/fs/autofs4/expire.c ++++ linux-2.6.22/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.22.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.22/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -335,8 +339,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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.patch b/patches/autofs4-2.6.22-v5-update.patch deleted file mode 100644 index 429b4a4..0000000 --- a/patches/autofs4-2.6.22-v5-update.patch +++ /dev/null @@ -1,108 +0,0 @@ -diff -Nurp linux-2.6.22.orig/fs/autofs4/root.c linux-2.6.22/fs/autofs4/root.c ---- linux-2.6.22.orig/fs/autofs4/root.c 2007-07-09 07:32:17.000000000 +0800 -+++ linux-2.6.22/fs/autofs4/root.c 2008-01-14 13:02:33.000000000 +0900 -@@ -19,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -291,8 +293,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -335,7 +336,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - -@@ -587,19 +588,20 @@ static struct dentry *autofs4_lookup(str - 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. -+ * 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); -+ d_instantiate(dentry, NULL); - } else { - struct autofs_info *ino = autofs4_dentry_ino(unhashed); - DPRINTK("rehash %p with %p", dentry, unhashed); -@@ -607,15 +609,17 @@ static struct dentry *autofs4_lookup(str - * 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. - */ -- if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { -+ 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"); - } -- d_rehash(unhashed); - dentry = unhashed; - } - -@@ -658,7 +662,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. -@@ -716,7 +720,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -844,7 +848,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; diff --git a/patches/autofs4-2.6.22.17-v5-update-20080924.patch b/patches/autofs4-2.6.22.17-v5-update-20080924.patch new file mode 100644 index 0000000..990be6f --- /dev/null +++ b/patches/autofs4-2.6.22.17-v5-update-20080924.patch @@ -0,0 +1,1682 @@ +--- 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); + +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.22.17.orig/fs/autofs4/expire.c ++++ linux-2.6.22.17/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.22.17.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.22.17/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -335,8 +339,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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-20080924.patch b/patches/autofs4-2.6.23-v5-update-20080924.patch new file mode 100644 index 0000000..7ac1245 --- /dev/null +++ b/patches/autofs4-2.6.23-v5-update-20080924.patch @@ -0,0 +1,1657 @@ +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.23.orig/fs/autofs4/expire.c ++++ linux-2.6.23/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.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); + +--- linux-2.6.23.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.23/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -335,8 +339,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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.patch b/patches/autofs4-2.6.23-v5-update.patch deleted file mode 100644 index 6cbe95e..0000000 --- a/patches/autofs4-2.6.23-v5-update.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff -Nurp linux-2.6.23.orig/fs/autofs4/root.c linux-2.6.23/fs/autofs4/root.c ---- linux-2.6.23.orig/fs/autofs4/root.c 2007-10-10 04:31:38.000000000 +0800 -+++ linux-2.6.23/fs/autofs4/root.c 2008-01-14 13:04:16.000000000 +0900 -@@ -19,6 +19,8 @@ - #include - #include "autofs_i.h" - -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -291,8 +293,7 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -335,7 +336,7 @@ static void *autofs4_follow_link(struct - 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); -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; - if (oz_mode || !lookup_type) - goto done; - diff --git a/patches/autofs4-2.6.24-v5-update-20080924.patch b/patches/autofs4-2.6.24-v5-update-20080924.patch new file mode 100644 index 0000000..b41678e --- /dev/null +++ b/patches/autofs4-2.6.24-v5-update-20080924.patch @@ -0,0 +1,1657 @@ +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.24.orig/fs/autofs4/expire.c ++++ linux-2.6.24/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.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); + +--- linux-2.6.24.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.24/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -333,8 +337,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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-20080924.patch b/patches/autofs4-2.6.24.4-v5-update-20080924.patch new file mode 100644 index 0000000..6efb28d --- /dev/null +++ b/patches/autofs4-2.6.24.4-v5-update-20080924.patch @@ -0,0 +1,1657 @@ +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.24.4.orig/fs/autofs4/expire.c ++++ linux-2.6.24.4/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.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); + +--- linux-2.6.24.4.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.24.4/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -333,8 +337,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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-20080924.patch b/patches/autofs4-2.6.25-v5-update-20080924.patch new file mode 100644 index 0000000..1508d77 --- /dev/null +++ b/patches/autofs4-2.6.25-v5-update-20080924.patch @@ -0,0 +1,1659 @@ +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.25.orig/fs/autofs4/expire.c ++++ linux-2.6.25/fs/autofs4/expire.c +@@ -73,8 +73,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 +259,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 +294,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 +320,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 +333,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 +345,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 +355,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 +438,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 +456,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 +@@ -435,9 +489,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.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); + +--- linux-2.6.25.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.25/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -338,8 +342,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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 +@@ -98,8 +98,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-20080924.patch b/patches/autofs4-2.6.26-v5-update-20080924.patch new file mode 100644 index 0000000..379ad5c --- /dev/null +++ b/patches/autofs4-2.6.26-v5-update-20080924.patch @@ -0,0 +1,1637 @@ +--- 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); + +--- linux-2.6.26.orig/fs/autofs4/autofs_i.h ++++ linux-2.6.26/fs/autofs4/autofs_i.h +@@ -52,7 +52,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 +71,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,7 +87,7 @@ 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 +@@ -112,8 +114,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 +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) +@@ -164,6 +163,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 *); +--- 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); +@@ -338,8 +342,9 @@ int autofs4_fill_super(struct super_bloc + 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; +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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) { +@@ -319,28 +416,25 @@ int autofs4_wait(struct autofs_sb_info * + } + + 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; +@@ -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; + } +--- linux-2.6.26.orig/fs/autofs4/expire.c ++++ linux-2.6.26/fs/autofs4/expire.c +@@ -259,13 +259,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 +294,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 +320,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 +333,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 +355,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 +381,7 @@ static struct dentry *autofs4_expire_ind + } + } + next: ++ spin_unlock(&sbi->fs_lock); + dput(dentry); + spin_lock(&dcache_lock); + next = next->next; +@@ -377,12 +392,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 +438,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 +456,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 +@@ -433,9 +489,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.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 +@@ -98,8 +98,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.9-v5-update-20080924.patch b/patches/autofs4-2.6.9-v5-update-20080924.patch new file mode 100644 index 0000000..f43356c --- /dev/null +++ b/patches/autofs4-2.6.9-v5-update-20080924.patch @@ -0,0 +1,3071 @@ +--- 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); + +--- 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 +@@ -40,14 +41,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 +53,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 +72,52 @@ 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 + ++#define AUTOFS_TYPE_INDIRECT 0x0001 ++#define AUTOFS_TYPE_DIRECT 0x0002 ++#define AUTOFS_TYPE_OFFSET 0x0004 ++ + 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 +143,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 +163,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 +175,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 +187,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 +231,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 *); +--- 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); +@@ -56,94 +56,152 @@ static int autofs4_check_mount(struct vf + mntget(mnt); + dget(dentry); + +- if (!follow_down(&mnt, &dentry)) ++ if (!autofs4_follow_mount(&mnt, &dentry)) + goto done; + +- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) +- ; +- + /* This is an autofs submount, we can't expire it */ + if (is_autofs4_dentry(dentry)) + goto done; + +- /* The big question */ +- if (may_umount_tree(mnt) == 0) +- status = 1; ++ /* 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; ++ } ++ ++ 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 +209,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; + } +@@ -213,10 +283,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 +294,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 +312,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 +320,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 +439,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 +457,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 +480,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_DIRECT) ++ 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; + } + +--- 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_DIRECT | 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 = 0; ++ 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_DIRECT ? ++ &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; + } +--- 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_DIRECT|AUTOFS_TYPE_OFFSET)) ++ 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_DIRECT)) ++ 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_DIRECT) ? ++ autofs_ptype_missing_direct : ++ autofs_ptype_missing_indirect; ++ else ++ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? ++ 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; + } +--- linux-2.6.9.orig/include/linux/auto_fs4.h ++++ linux-2.6.9/include/linux/auto_fs4.h +@@ -19,18 +19,37 @@ + #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) */ ++/* 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 +66,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) + + +--- 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! */ +--- 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); +--- 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); +--- 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) +--- 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; + } +--- 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 */ + +--- 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) +--- 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 */ + } + +--- 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) diff --git a/patches/autofs4-2.6.9-v5-update.patch b/patches/autofs4-2.6.9-v5-update.patch deleted file mode 100644 index a7373f9..0000000 --- a/patches/autofs4-2.6.9-v5-update.patch +++ /dev/null @@ -1,2528 +0,0 @@ -diff -Nurp linux-2.6.9.orig/fs/autofs/autofs_i.h linux-2.6.9/fs/autofs/autofs_i.h ---- linux-2.6.9.orig/fs/autofs/autofs_i.h 2004-10-19 05:54:40.000000000 +0800 -+++ linux-2.6.9/fs/autofs/autofs_i.h 2008-01-14 12:42:29.000000000 +0900 -@@ -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 */ - -diff -Nurp linux-2.6.9.orig/fs/autofs/dirhash.c linux-2.6.9/fs/autofs/dirhash.c ---- linux-2.6.9.orig/fs/autofs/dirhash.c 2004-10-19 05:53:12.000000000 +0800 -+++ linux-2.6.9/fs/autofs/dirhash.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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! */ -diff -Nurp linux-2.6.9.orig/fs/autofs/init.c linux-2.6.9/fs/autofs/init.c ---- linux-2.6.9.orig/fs/autofs/init.c 2004-10-19 05:54:38.000000000 +0800 -+++ linux-2.6.9/fs/autofs/init.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.9.orig/fs/autofs/inode.c linux-2.6.9/fs/autofs/inode.c ---- linux-2.6.9.orig/fs/autofs/inode.c 2004-10-19 05:54:40.000000000 +0800 -+++ linux-2.6.9/fs/autofs/inode.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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; - } -diff -Nurp linux-2.6.9.orig/fs/autofs/waitq.c linux-2.6.9/fs/autofs/waitq.c ---- linux-2.6.9.orig/fs/autofs/waitq.c 2004-10-19 05:53:43.000000000 +0800 -+++ linux-2.6.9/fs/autofs/waitq.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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 */ - } - -diff -Nurp linux-2.6.9.orig/fs/autofs4/autofs_i.h linux-2.6.9/fs/autofs4/autofs_i.h ---- linux-2.6.9.orig/fs/autofs4/autofs_i.h 2004-10-19 05:54:38.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/autofs_i.h 2008-01-14 12:42:29.000000000 +0900 -@@ -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 -@@ -40,14 +41,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 +53,11 @@ struct autofs_info { - - int flags; - -+ struct list_head rehash; -+ - struct autofs_sb_info *sbi; - unsigned long last_used; -+ atomic_t count; - - mode_t mode; - size_t size; -@@ -79,9 +75,15 @@ struct autofs_wait_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; -+ unsigned int hash; -+ unsigned int len; - char *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; -@@ -89,19 +91,30 @@ 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; - 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 rehash_lock; -+ struct list_head rehash_list; - }; - - static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) -@@ -126,9 +139,18 @@ 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); -+ } - -- return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || -- (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); -+ return pending; - } - - static inline void autofs4_copy_atime(struct file *src, struct file *dst) -@@ -153,6 +175,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 +187,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 +231,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 *); -diff -Nurp linux-2.6.9.orig/fs/autofs4/expire.c linux-2.6.9/fs/autofs4/expire.c ---- linux-2.6.9.orig/fs/autofs4/expire.c 2004-10-19 05:54:39.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/expire.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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,19 +56,21 @@ static int autofs4_check_mount(struct vf - mntget(mnt); - dget(dentry); - -- if (!follow_down(&mnt, &dentry)) -+ if (!autofs4_follow_mount(&mnt, &dentry)) - goto done; - -- while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) -- ; -- - /* This is an autofs submount, we can't expire it */ - if (is_autofs4_dentry(dentry)) - goto done; - -- /* The big question */ -- if (may_umount_tree(mnt) == 0) -- status = 1; -+ /* 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; -+ } -+ -+ status = 0; - done: - DPRINTK("returning = %d", status); - mntput(mnt); -@@ -76,74 +78,130 @@ done: - 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 +209,68 @@ 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; - -- if (this_parent != parent) { -- next = this_parent->d_child.next; -- this_parent = this_parent->d_parent; -- goto resume; -+ 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 */ -+ ino->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ return root; - } -- spin_unlock(&dcache_lock); -+ spin_unlock(&sbi->fs_lock); -+ dput(root); - - return NULL; - } -@@ -213,10 +281,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; -@@ -240,7 +308,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,33 +316,49 @@ static struct dentry *autofs4_expire(str - dentry = dget(dentry); - spin_unlock(&dcache_lock); - -- /* Case 1: indirect mount or top level direct mount */ -+ /* -+ * 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)) -+ /* Can we umount this guy */ -+ if (autofs4_mount_busy(mnt, dentry)) - goto next; - -- /* Can we umount this guy */ -- if (autofs4_check_mount(mnt, dentry)) { -+ /* Can we expire this guy */ -+ if (autofs4_can_expire(dentry, timeout, do_now)) { - expired = dentry; - break; - } - 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; -+ /* 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); -+ -+ /* Set this flag early to catch sys_chdir and the like */ -+ inf->flags |= AUTOFS_INF_EXPIRING; -+ spin_unlock(&sbi->fs_lock); -+ expired = dentry; -+ break; - } -- /* Case 3: direct mount, expire individual leaves */ -+ spin_unlock(&sbi->fs_lock); -+ /* -+ * Case 3: pseudo direct mount, expire individual leaves -+ * (autofs-4.1). -+ */ - } else { - expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); - if (expired) { -@@ -288,7 +372,7 @@ next: - next = next->next; - } - -- if ( expired ) { -+ if (expired) { - DPRINTK("returning %p %.*s", - expired, (int)expired->d_name.len, expired->d_name.name); - spin_lock(&dcache_lock); -@@ -316,7 +400,7 @@ int autofs4_expire_run(struct super_bloc - 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; -@@ -342,17 +426,22 @@ 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_DIRECT) -+ 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; -+ ino->flags |= AUTOFS_INF_EXPIRING; - ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); -- de_info->flags &= ~AUTOFS_INF_EXPIRING; -+ ino->flags &= ~AUTOFS_INF_EXPIRING; - dput(dentry); - } -- -+ - return ret; - } - -diff -Nurp linux-2.6.9.orig/fs/autofs4/init.c linux-2.6.9/fs/autofs4/init.c ---- linux-2.6.9.orig/fs/autofs4/init.c 2004-10-19 05:54:39.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/init.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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) -diff -Nurp linux-2.6.9.orig/fs/autofs4/inode.c linux-2.6.9/fs/autofs4/inode.c ---- linux-2.6.9.orig/fs/autofs4/inode.c 2004-10-19 05:54:40.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/inode.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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 - -@@ -46,7 +49,10 @@ struct autofs_info *autofs4_init_ino(str - ino->dentry = NULL; - ino->size = 0; - -+ INIT_LIST_HEAD(&ino->rehash); -+ - ino->last_used = jiffies; -+ atomic_set(&ino->count, 0); - - ino->sbi = sbi; - -@@ -65,10 +71,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 +91,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 ) -+ if (!sbi->catatonic) - autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ - -+ /* 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 +214,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 +276,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_DIRECT | AUTOFS_TYPE_OFFSET; -+ break; - default: - return 1; - } -@@ -180,6 +303,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 +315,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 +325,22 @@ 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 = 0; -+ 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->rehash_lock); -+ INIT_LIST_HEAD(&sbi->rehash_list); - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; -@@ -219,38 +353,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_DIRECT ? -+ &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 +405,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 +427,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; - } -diff -Nurp linux-2.6.9.orig/fs/autofs4/root.c linux-2.6.9/fs/autofs4/root.c ---- linux-2.6.9.orig/fs/autofs4/root.c 2004-10-19 05:53:06.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/root.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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,7 +19,8 @@ - #include - #include "autofs_i.h" - --static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *, struct nameidata *); -+#define MOUNT_TRIGGER_FLAGS (LOOKUP_CONTINUE|LOOKUP_DIRECTORY|LOOKUP_ACCESS) -+ - 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 *); -@@ -29,8 +30,8 @@ static int autofs4_dir_open(struct inode - 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 *); - - struct file_operations autofs4_root_operations = { - .open = dcache_dir_open, -@@ -47,16 +48,24 @@ struct file_operations autofs4_dir_opera - .readdir = autofs4_dir_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, -@@ -82,86 +91,7 @@ static int autofs4_root_readdir(struct f - - 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; -+ return dcache_readdir(file, dirent, filldir); - } - - static int autofs4_dir_open(struct inode *inode, struct file *file) -@@ -169,8 +99,16 @@ static int autofs4_dir_open(struct inode - 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); - -@@ -179,12 +117,15 @@ static int autofs4_dir_open(struct inode - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ 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; -+ int empty, ret; - - /* In case there are stale directory dentrys from a failed mount */ - spin_lock(&dcache_lock); -@@ -195,10 +136,14 @@ static int autofs4_dir_open(struct inode - d_invalidate(dentry); - - nd.flags = LOOKUP_DIRECTORY; -- status = (dentry->d_op->d_revalidate)(dentry, &nd); -+ ret = (dentry->d_op->d_revalidate)(dentry, &nd); - -- if (!status) -- return -ENOENT; -+ if (ret <= 0) { -+ if (ret < 0) -+ status = ret; -+ dcache_dir_close(inode, file); -+ goto out; -+ } - } - - if (d_mountpoint(dentry)) { -@@ -206,24 +151,32 @@ static int autofs4_dir_open(struct inode - struct vfsmount *fp_mnt = mntget(mnt); - struct dentry *fp_dentry = dget(dentry); - -- while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_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)) { -- file->private_data = NULL; -- return status; -+ dcache_dir_close(inode, file); -+ goto out; - } -- file->private_data = fp; -+ cursor->d_fsdata = fp; - } --out: - 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); -@@ -233,26 +186,28 @@ static int autofs4_dir_close(struct inod - - if (autofs4_ispending(dentry)) { - DPRINTK("dentry busy"); -- return -EBUSY; -+ status = -EBUSY; -+ goto out; - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -- -- if (!fp) -- return -ENOENT; -- -+ struct file *fp = cursor->d_fsdata; -+ if (!fp) { -+ status = -ENOENT; -+ goto out; -+ } - filp_close(fp, current->files); -- file->private_data = NULL; - } - out: -- return 0; -+ 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", -@@ -267,7 +222,7 @@ static int autofs4_dir_readdir(struct fi - } - - if (d_mountpoint(dentry)) { -- struct file *fp = file->private_data; -+ struct file *fp = cursor->d_fsdata; - - if (!fp) - return -ENOENT; -@@ -282,28 +237,34 @@ static int autofs4_dir_readdir(struct fi - return status; - } - out: -- return autofs4_dcache_readdir(file, dirent, filldir); -+ return 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) -+static int try_to_fill_dentry(struct dentry *dentry, int flags) - { -- struct autofs_info *de_info = autofs4_dentry_ino(dentry); -+ 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 (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { -+ 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); -- -- return 0; -+ -+ /* -+ * 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", -@@ -318,23 +279,18 @@ 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) || -- current->link_count) { -+ } else if (flags & MOUNT_TRIGGER_FLAGS || current->link_count) { - DPRINTK("waiting for mount name=%.*s", - dentry->d_name.len, dentry->d_name.name); - -@@ -349,19 +305,83 @@ 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 status; -+} -+ -+/* 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); -+ -+ /* If it's our master or we shouldn't trigger a mount we're done */ -+ lookup_type = nd->flags & MOUNT_TRIGGER_FLAGS; -+ if (oz_mode || !lookup_type) -+ 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); -+ -+ DPRINTK("request done status=%d", status); -+ } -+ -+ /* -+ * 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)) { -+ 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; -+ } -+ spin_unlock(&dcache_lock); -+ -+done: -+ return 0; -+ -+out_error: -+ path_release(nd); -+ return status; - } - - /* -@@ -370,47 +390,72 @@ 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 */ - 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 */ -+ 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; -+ -+ /* -+ * 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; - } - - /* 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 +465,15 @@ 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->rehash_lock); -+ if (!list_empty(&inf->rehash)) -+ list_del(&inf->rehash); -+ spin_unlock(&sbi->rehash_lock); -+ } -+ - inf->dentry = NULL; - inf->inode = NULL; - -@@ -439,58 +493,138 @@ 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_unhashed(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->rehash_lock); -+ head = &sbi->rehash_list; -+ list_for_each(p, head) { -+ struct autofs_info *ino; -+ struct dentry *dentry; -+ struct qstr *qstr; -+ -+ ino = list_entry(p, struct autofs_info, rehash); -+ 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)) { -+ 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(&dcache_lock); -+ return dentry; -+ } -+next: -+ spin_unlock(&dentry->d_lock); -+ } -+ spin_unlock(&sbi->rehash_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 dentry *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; -+ unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); -+ if (!unhashed) { -+ /* -+ * 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_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. -+ */ -+ 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; -+ } - - 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); -@@ -509,19 +643,45 @@ static struct dentry *autofs4_root_looku - 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 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 dentry; - - return NULL; - } -@@ -532,6 +692,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; - -@@ -556,7 +717,7 @@ static int autofs4_dir_symlink(struct in - strcpy(cp, symname); - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -565,6 +726,10 @@ 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; - - dir->i_mtime = CURRENT_TIME; -@@ -578,9 +743,10 @@ 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 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. - * - * 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 +757,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 +775,12 @@ static int autofs4_dir_unlink(struct ino - - dir->i_mtime = CURRENT_TIME; - -- d_drop(dentry); -+ spin_lock(&dcache_lock); -+ spin_lock(&sbi->rehash_lock); -+ list_add(&ino->rehash, &sbi->rehash_list); -+ spin_unlock(&sbi->rehash_lock); -+ __d_drop(dentry); -+ spin_unlock(&dcache_lock); - - return 0; - } -@@ -612,7 +789,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 +802,18 @@ 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); - __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 +827,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) ) -@@ -652,7 +841,7 @@ static int autofs4_dir_mkdir(struct inod - return -ENOSPC; - - inode = autofs4_get_inode(dir->i_sb, ino); -- d_instantiate(dentry, inode); -+ d_add(dentry, inode); - - if (dir == dir->i_sb->s_root->d_inode) - dentry->d_op = &autofs4_root_dentry_operations; -@@ -661,6 +850,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; -@@ -744,7 +937,7 @@ static inline int autofs4_ask_umount(str - { - int status = 0; - -- if (may_umount(mnt) == 0) -+ if (may_umount(mnt)) - status = 1; - - DPRINTK("returning %d", status); -diff -Nurp linux-2.6.9.orig/fs/autofs4/waitq.c linux-2.6.9/fs/autofs4/waitq.c ---- linux-2.6.9.orig/fs/autofs4/waitq.c 2004-10-19 05:55:27.000000000 +0800 -+++ linux-2.6.9/fs/autofs4/waitq.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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 -@@ -33,7 +33,7 @@ void autofs4_catatonic_mode(struct autof - 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); -@@ -41,11 +41,8 @@ void autofs4_catatonic_mode(struct autof - 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; - shrink_dcache_sb(sbi->sb); - } - -@@ -88,7 +85,11 @@ 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; - size_t pktsz; - - DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", -@@ -98,8 +99,11 @@ static void autofs4_notify_daemon(struct - - 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); - -@@ -107,8 +111,11 @@ static void autofs4_notify_daemon(struct - 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; -+ break; -+ } -+ case autofs_ptype_expire_multi: -+ { -+ struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi; - - pktsz = sizeof(*ep); - -@@ -116,7 +123,34 @@ static void autofs4_notify_daemon(struct - ep->len = wq->len; - memcpy(ep->name, wq->name, wq->len); - ep->name[wq->len] = '\0'; -- } else { -+ 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->len; -+ memcpy(packet->name, wq->name, wq->len); -+ packet->name[wq->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; - } -@@ -157,43 +191,95 @@ static int autofs4_getpath(struct autofs - return len; - } - -+static struct autofs_wait_queue * -+autofs4_find_wait(struct autofs_sb_info *sbi, -+ char *name, unsigned int hash, unsigned int len) -+{ -+ struct autofs_wait_queue *wq = NULL; -+ -+ for (wq = sbi->queues ; wq ; wq = wq->next) { -+ if (wq->hash == hash && -+ wq->len == len && -+ wq->name && !memcmp(wq->name, name, len)) -+ break; -+ } -+ return wq; -+} -+ - int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, - enum autofs_notify notify) - { -+ struct autofs_info *ino; - struct autofs_wait_queue *wq; - char *name; -- int len, status; -+ unsigned int len = 0; -+ unsigned int hash = 0; -+ int status, type; - - /* In catatonic mode, we don't wait for nobody */ -- if ( sbi->catatonic ) -+ if (sbi->catatonic) - 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_DIRECT)) -+ len = sprintf(name, "%p", dentry); -+ else { -+ len = autofs4_getpath(sbi, dentry, &name); -+ if (!len) { -+ kfree(name); -+ return -ENOENT; -+ } - } -+ hash = full_name_hash(name, len); - - if (down_interruptible(&sbi->wq_sem)) { - kfree(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; -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ ino = autofs4_dentry_ino(dentry); -+ if (!wq && ino && notify == NFY_NONE) { -+ /* -+ * Either we've betean the pending expire to post it's -+ * wait or it finished while we waited on the mutex. -+ * So we need to wait till either, the wait appears -+ * or the expire finishes. -+ */ -+ -+ while (ino->flags & AUTOFS_INF_EXPIRING) { -+ up(&sbi->wq_sem); -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ/10); -+ if (down_interruptible(&sbi->wq_sem)) { -+ kfree(name); -+ return -EINTR; -+ } -+ wq = autofs4_find_wait(sbi, name, hash, len); -+ if (wq) -+ break; -+ } -+ -+ /* -+ * 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. -+ */ -+ if (!wq) { -+ kfree(name); -+ up(&sbi->wq_sem); -+ return 0; -+ } - } - -- if ( !wq ) { -+ if (!wq) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); -- if ( !wq ) { -+ if (!wq) { - kfree(name); - up(&sbi->wq_sem); - return -ENOMEM; -@@ -205,25 +291,44 @@ 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->hash = hash; - wq->name = name; - wq->len = len; -+ 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); - up(&sbi->wq_sem); - -- DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d", -+ 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_DIRECT) ? -+ autofs_ptype_missing_direct : -+ autofs_ptype_missing_indirect; -+ else -+ type = (sbi->type & AUTOFS_TYPE_DIRECT) ? -+ 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); -+ - /* autofs4_notify_daemon() may block */ -- if (notify != NFY_NONE) { -- autofs4_notify_daemon(sbi,wq, -- notify == NFY_MOUNT ? -- autofs_ptype_missing : -- autofs_ptype_expire_multi); -- } -+ autofs4_notify_daemon(sbi, wq, type); - } 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); - } -@@ -275,12 +380,12 @@ 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; - } -diff -Nurp linux-2.6.9.orig/fs/namei.c linux-2.6.9/fs/namei.c ---- linux-2.6.9.orig/fs/namei.c 2004-10-19 05:53:46.000000000 +0800 -+++ linux-2.6.9/fs/namei.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.9.orig/fs/namespace.c linux-2.6.9/fs/namespace.c ---- linux-2.6.9.orig/fs/namespace.c 2004-10-19 05:54:37.000000000 +0800 -+++ linux-2.6.9/fs/namespace.c 2008-01-14 12:42:29.000000000 +0900 -@@ -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); -diff -Nurp linux-2.6.9.orig/include/linux/auto_fs4.h linux-2.6.9/include/linux/auto_fs4.h ---- linux-2.6.9.orig/include/linux/auto_fs4.h 2004-10-19 05:53:08.000000000 +0800 -+++ linux-2.6.9/include/linux/auto_fs4.h 2008-01-14 12:42:29.000000000 +0900 -@@ -19,18 +19,37 @@ - #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) */ -+/* 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,7 +66,37 @@ 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)