[autofs] [PATCH] autofs4: Use no_printk() for no-op DPRINTK() and use __VA_ARGS__ too
Use no_printk() for autofs's no-op DPRINTK() to prevent unused statements from becoming accidentally obsolete, and use __VA_ARGS__ too as that's the standard way. Signed-off-by: David Howells dhowe...@redhat.com Signed-off-by: Ian Kent ra...@themaw.net --- fs/autofs4/autofs_i.h | 10 +++--- fs/autofs4/waitq.c|3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 475f9c5..c3a419f 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -40,13 +40,17 @@ /* #define DEBUG */ #ifdef DEBUG -#define DPRINTK(fmt, args...) \ +#define DPRINTK(fmt, ...) \ do { \ printk(KERN_DEBUG pid %d: %s: fmt \n, \ - current-pid, __func__, ##args);\ + current-pid, __func__, ##__VA_ARGS__); \ } while (0) #else -#define DPRINTK(fmt, args...) do {} while (0) +#define DPRINTK(fmt, ...) \ +do { \ + no_printk(KERN_DEBUG pid %d: %s: fmt \n, \ + current-pid, __func__, ##__VA_ARGS__); \ +} while (0) #endif #define AUTOFS_WARN(fmt, args...) \ diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 2543598..6313a0d 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -104,7 +104,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, size_t pktsz; DPRINTK(wait id = 0x%08lx, name = %.*s, type=%d, - wq-wait_queue_token, wq-name.len, wq-name.name, type); + (unsigned long)wq-wait_queue_token, + wq-name.len, wq-name.name, type); memset(pkt,0,sizeof pkt); /* For security reasons */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH] VFS: Fix automount for negative autofs dentries
Autofs may set the DCACHE_NEED_AUTOMOUNT flag on negative dentries. These need attention from the automounter daemon regardless of the LOOKUP_FOLLOW flag. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/namei.c | 27 +++ 1 files changed, 19 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 0223c41..93e221e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -777,18 +777,29 @@ static int follow_automount(struct path *path, unsigned flags, if ((flags LOOKUP_NO_AUTOMOUNT) !(flags LOOKUP_CONTINUE)) return -EISDIR; /* we actually want to stop here */ - /* We want to mount if someone is trying to open/create a file of any -* type under the mountpoint, wants to traverse through the mountpoint -* or wants to open the mounted directory. -* -* We don't want to mount if someone's just doing a stat and they've + /* We don't want to mount if someone's just doing a stat and they've * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and * appended a '/' to the name. */ - if (!(flags LOOKUP_FOLLOW) - !(flags (LOOKUP_CONTINUE | LOOKUP_DIRECTORY | - LOOKUP_OPEN | LOOKUP_CREATE))) + if (!(flags LOOKUP_FOLLOW)) { + /* We do, however, want to mount if someone wants to open or +* create a file of any type under the mountpoint, wants to +* traverse through the mountpoint or wants to open the mounted +* directory. +*/ + if (flags (LOOKUP_CONTINUE | LOOKUP_DIRECTORY | +LOOKUP_OPEN | LOOKUP_CREATE)) + goto need_automount; + + /* Also, autofs may mark negative dentries as being automount +* points. These will need the attentions of the daemon to +* instantiate them before they can be used. +*/ + if (!path-dentry-d_inode) + goto need_automount; return -EISDIR; + } +need_automount: current-total_link_count++; if (current-total_link_count = 40) ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH] VFS: Fix automount for negative autofs dentries
Christoph Hellwig h...@infradead.org wrote: would do the same. But is much less obvious. The LOOKUP_FOLLOW flag is the primary reason for this statement. The rest are subordinate and would be wholly irrelevant if LOOKUP_FOLLOW was to be removed from the list. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH] autofs4: Use no_printk() for no-op DPRINTK() and use __VA_ARGS__ too
Use no_printk() for autofs's no-op DPRINTK() to prevent unused statements from becoming accidentally obsolete, and use __VA_ARGS__ too as that's the standard way. Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h | 10 +++--- fs/autofs4/waitq.c|3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 475f9c5..c3a419f 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -40,13 +40,17 @@ /* #define DEBUG */ #ifdef DEBUG -#define DPRINTK(fmt, args...) \ +#define DPRINTK(fmt, ...) \ do { \ printk(KERN_DEBUG pid %d: %s: fmt \n, \ - current-pid, __func__, ##args);\ + current-pid, __func__, ##__VA_ARGS__); \ } while (0) #else -#define DPRINTK(fmt, args...) do {} while (0) +#define DPRINTK(fmt, ...) \ +do { \ + no_printk(KERN_DEBUG pid %d: %s: fmt \n, \ + current-pid, __func__, ##__VA_ARGS__); \ +} while (0) #endif #define AUTOFS_WARN(fmt, args...) \ diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 2543598..6313a0d 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -104,7 +104,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, size_t pktsz; DPRINTK(wait id = 0x%08lx, name = %.*s, type=%d, - wq-wait_queue_token, wq-name.len, wq-name.name, type); + (unsigned long)wq-wait_queue_token, + wq-name.len, wq-name.name, type); memset(pkt,0,sizeof pkt); /* For security reasons */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] Bug in autofs4_d_automount()?
Hi Ian, At the top of autofs4_d_automount() you have: /* The daemon never triggers a mount. */ if (autofs4_oz_mode(sbi)) return NULL; I think this should be returning -EISDIR. If by some chance we do get here in Oz mode, this will cause the kernel to just loop forever. A return of NULL is meant to indicate that you got a collision and that it should recheck the mountpoint - but it does not advance path in follow_managed(). -EISDIR is the return to indicate this is to be treated as a normal directory. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH] AUTOFS4: Fix the return from autofs4_d_automount() and simplify autofs4_d_manage()
autofs4_d_automount() returns 0 if it detects that the calling process is in Oz mode (ie. it's the autofs userspace daemon). This return, however, is meant to indicate to follow_automount() that the caller should retry the check on the the current path point. In the Oz mode case, this is a bad idea because nothing has changed on the path, and follow_managed() will just repeat until follow_automount() hits the total_link_count limit and returns -ELOOP. What it should do is return -EISDIR to indicate to the callers that actually it wants the daemon to see this directory as an ordinary directory. Now, given that change outlined above, it is then unnecessary for autofs4_d_manage() to return -EISDIR if the current path point is not a mountpoint. If it returns 0 instead, and the path point isn't a mountpoint, then follow_managed() will skip the attempt to transit to the mounted filesystem and proceed to call autofs4_d_automount(), which will return -EISDIR. Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/root.c |9 ++--- 1 files changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index f55ae23..a6dc11c 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -334,7 +334,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path) /* The daemon never triggers a mount. */ if (autofs4_oz_mode(sbi)) - return NULL; + return ERR_PTR(-EISDIR); /* * If an expire request is pending everyone must wait. @@ -435,13 +435,8 @@ int autofs4_d_manage(struct dentry *dentry, bool rcu_walk) dentry, dentry-d_name.len, dentry-d_name.name); /* The daemon never waits. */ - if (autofs4_oz_mode(sbi)) { - if (rcu_walk) - return 0; - if (!d_mountpoint(dentry)) - return -EISDIR; + if (autofs4_oz_mode(sbi)) return 0; - } /* We need to sleep, so we need pathwalk to be in ref-mode */ if (rcu_walk) ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] stat -L triggering mount (behavior change starting with 2.6.38-rc1)
Hi Leonardo, Could you send us a strace of ls -l on your autofs directory? I think the problem is not lstat() calls, but rather getxattr() calls. Thanks, David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] stat -L triggering mount (behavior change starting with 2.6.38-rc1)
Leonardo Chiquitto leonardo.li...@gmail.com wrote: open(/data, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 fcntl(3, F_GETFD) = 0x1 (flags FD_CLOEXEC) getdents64(3, /* 3 entries */, 32768) = 72 lstat(/data/isos, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 lgetxattr(/data/isos, security.selinux, 0x62ad60, 255) = -1 EOPNOTSUPP (Operation not supported) getxattr(/data/isos, system.posix_acl_access, 0x0, 0) = -1 EOPNOTSUPP (Operation not supported) getdents64(3, /* 0 entries */, 32768) = 0 Yeah, I suspect the getxattr() is the problem. ls calls libacl to get the Posix ACL of the target file, but that uses the getxattr() which asserts LOOKUP_FOLLOW during the pathwalk, causing the automount unconditionally:-/ I'm discussing this with the coreutils and acl package maintainers to see if we can fix it in userspace. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 2/3] Make VFS handle mount autoexpiry
Make the VFS handle mount autoexpiry, rather than doing it in the filesystems (such as AFS, CIFS and NFS). This simplifies the reference counting, since do_add_mount() once again adds mounts to the expiration list, and simplifies the filesystems since they no longer have to do anything barring set MNT_EXPIRABLE on an expirable mount before returning it. Additionally, provide a tuning knob to set the periodicity of the reaper in seconds: /proc/sys/fs/mount-expiry-period The default is 10 minutes. The NFS sysctl (nfs_mountpoint_timeout) is removed in its favour. Signed-off-by: David Howells dhowe...@redhat.com --- Documentation/filesystems/vfs.txt |7 +- fs/afs/internal.h |1 fs/afs/mntpt.c| 60 ++--- fs/afs/super.c|1 fs/cifs/cifs_dfs_ref.c| 53 ++- fs/cifs/cifsfs.c |3 - fs/cifs/cifsproto.h |1 fs/namei.c|8 +-- fs/namespace.c| 106 - fs/nfs/client.c |1 fs/nfs/namespace.c| 26 - fs/nfs/sysctl.c |7 -- include/linux/mount.h |5 +- kernel/sysctl.c | 11 14 files changed, 76 insertions(+), 214 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index c6878a0..3a40c31 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -943,10 +943,9 @@ struct dentry_operations { ordinary directory and returned to pathwalk to continue walking. If a vfsmount is returned, the caller will attempt to mount it on the - mountpoint and will remove the vfsmount from its expiration list in - the case of failure. The vfsmount should be returned with 2 refs on - it to prevent automatic expiration - the caller will clean up the - additional ref. + mountpoint and will clean it up on failure. If mnt_expiry_mark is set + on the vfsmount, the caller will add it to the global expiration list + if successfully mounted and clear the mark. This function is only used if DCACHE_NEED_AUTOMOUNT is set on the dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5a9b684..cb7d2c7 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -594,7 +594,6 @@ extern const struct file_operations afs_mntpt_file_operations; extern struct vfsmount *afs_d_automount(struct path *); extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *); -extern void afs_mntpt_kill_timer(void); /* * proc.c diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index aa59184..74b43b9 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); static int afs_mntpt_open(struct inode *inode, struct file *file); -static void afs_mntpt_expiry_timed_out(struct work_struct *work); const struct file_operations afs_mntpt_file_operations = { .open = afs_mntpt_open, @@ -41,11 +40,6 @@ const struct inode_operations afs_autocell_inode_operations = { .getattr= afs_getattr, }; -static LIST_HEAD(afs_vfsmounts); -static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); - -static unsigned long afs_mntpt_expiry_timeout = 10 * 60; - /* * check a symbolic link to see whether it actually encodes a mountpoint * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately @@ -136,11 +130,12 @@ static int afs_mntpt_open(struct inode *inode, struct file *file) /* * create a vfsmount to be automounted */ -static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) +struct vfsmount *afs_d_automount(struct path *mountpoint) { struct afs_super_info *super; struct vfsmount *mnt; struct afs_vnode *vnode; + struct dentry *mntpt = mountpoint-dentry; struct page *page; char *devname, *options; bool rwpath = false; @@ -219,6 +214,9 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) mnt = vfs_kern_mount(afs_fs_type, 0, devname, options); _debug(--- mount result %p ---, mnt); + if (!IS_ERR(mnt)) + mnt-mnt_expiry_mark = 1; + free_page((unsigned long) devname); free_page((unsigned long) options); _leave( = %p, mnt); @@ -234,51 +232,3 @@ error_no_devname: _leave( = %d, ret); return ERR_PTR(ret); } - -/* - * handle an automount point - */ -struct vfsmount *afs_d_automount(struct path *path) -{ - struct vfsmount *newmnt; - - _enter({%s,%s}, path-mnt-mnt_devname, path-dentry
[autofs] [PATCH] autofs4: Use no_printk() for no-op DPRINTK() and use __VA_ARGS__ too
Use no_printk() for autofs's no-op DPRINTK() to prevent unused statements from becoming accidentally obsolete, and use __VA_ARGS__ too as that's the standard way. Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h | 10 +++--- fs/autofs4/waitq.c|3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 54f9237..ef89828 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -40,13 +40,17 @@ /* #define DEBUG */ #ifdef DEBUG -#define DPRINTK(fmt, args...) \ +#define DPRINTK(fmt, ...) \ do { \ printk(KERN_DEBUG pid %d: %s: fmt \n, \ - current-pid, __func__, ##args);\ + current-pid, __func__, ##__VA_ARGS__); \ } while (0) #else -#define DPRINTK(fmt, args...) do {} while (0) +#define DPRINTK(fmt, ...) \ +do { \ + no_printk(KERN_DEBUG pid %d: %s: fmt \n, \ + current-pid, __func__, ##__VA_ARGS__); \ +} while (0) #endif #define AUTOFS_WARN(fmt, args...) \ diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 5601005..4d169cf 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -104,7 +104,8 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, size_t pktsz; DPRINTK(wait id = 0x%08lx, name = %.*s, type=%d, - wq-wait_queue_token, wq-name.len, wq-name.name, type); + (unsigned long)wq-wait_queue_token, + wq-name.len, wq-name.name, type); memset(pkt,0,sizeof pkt); /* For security reasons */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link() [ver #4]
Al Viro v...@zeniv.linux.org.uk wrote: OK, umount_tree bug (the source of AFS leak) got presumably fixed in #untested. Have fun... Works for me. Acked-by: David Howells dhowe...@redhat.com ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH] autofs4: Merge the remaining dentry ops tables
Merge the remaining autofs4 dentry ops tables. It doesn't matter if d_automount and d_manage are present on something that's not mountable or holdable as these ops are only used if the appropriate flags are set in dentry-d_flags. Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |1 - fs/autofs4/inode.c|4 +--- fs/autofs4/root.c | 13 ++--- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index c28085c..1f016bf 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -207,7 +207,6 @@ extern const struct inode_operations autofs4_dir_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; extern const struct dentry_operations autofs4_dentry_operations; -extern const struct dentry_operations autofs4_mount_dentry_operations; /* VFS automount flags management functions */ diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 3ecd2e2..2e80cf0 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -304,10 +304,8 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_dput; } - if (autofs_type_trigger(sbi-type)) { - d_set_d_op(root, autofs4_mount_dentry_operations); + if (autofs_type_trigger(sbi-type)) __managed_dentry_set_managed(root); - } root_inode-i_fop = autofs4_root_operations; root_inode-i_op = autofs4_dir_inode_operations; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index dbd9551..0db9d53 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -66,13 +66,7 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; -/* For dentries that don't initiate mounting */ const struct dentry_operations autofs4_dentry_operations = { - .d_release = autofs4_dentry_release, -}; - -/* For dentries that do initiate mounting */ -const struct dentry_operations autofs4_mount_dentry_operations = { .d_automount= autofs4_d_automount, .d_manage = autofs4_d_manage, .d_release = autofs4_dentry_release, @@ -512,10 +506,8 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s return ERR_PTR(-ENOENT); /* Mark entries in the root as mount triggers */ - if (autofs_type_indirect(sbi-type) IS_ROOT(dentry-d_parent)) { - d_set_d_op(dentry, autofs4_mount_dentry_operations); + if (autofs_type_indirect(sbi-type) IS_ROOT(dentry-d_parent)) __managed_dentry_set_managed(dentry); - } ino = autofs4_init_ino(NULL, sbi, 0555); if (!ino) @@ -848,8 +840,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p) int is_autofs4_dentry(struct dentry *dentry) { return dentry dentry-d_inode - (dentry-d_op == autofs4_mount_dentry_operations || -dentry-d_op == autofs4_dentry_operations) + dentry-d_op == autofs4_dentry_operations dentry-d_fsdata != NULL; } ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] autofs4_d_automount() can change path-dentry param
Hi Ian, I've just noticed that autofs4_d_automount() can change the dentry pointer in the path parameter (via autofs4_mountpoint_changed()). Is this just doing a straight substitution of one dentry for its equivalent? I don't think it'll be a problem for follow_automount() and follow_managed(), provided the dentry stays in the same namespace - but if we eliminate the vfsmount pointer and just pass the dentry pointer in to d_automount(), you won't be able to do this anymore. Would it work to simply return NULL here and hope the recheck picks up the substitution? David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link()
Nick Piggin npig...@gmail.com wrote: You still have to notice that it is .d_automount in rcu-walk mode, and bail out if it is. I can't see where you do that. follow_managed(), and thus follow_automount() and -d_automount(), are never reached in rcu_walk mode, from what I can tell of the code. There are two places follow_managed() is called: (1) do_lookup() - where follow_managed() is only called in the else-part of an if-statement contingent on a check of LOOKUP_RCU. (2) do_last() - where follow_managed() is subsequent to a mutex having been taken, so rcu-walk mode must have been exited prior to this as the process may have needed to sleep. At least, I'm assuming you may not sleep whilst in rcu-walk mode. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 00/18] Introduce automount support in the VFS [ver #4]
Al Viro v...@zeniv.linux.org.uk wrote: As the result, we stop abusing do_add_mount() in there. Moreover, with pending mnt_devname nfs rework we will be able to get rid of passing vfsmount to -d_automount(), AFAICT, which would be nice... BTW, what do you need vfsmount for in case of the only -d_manage() instance you've got? I'm not sure it's strictly necessary. However, is it or will it be possible for autofs to have per-namespace daemons? I suspect I can probably downgrade the path pointer to a dentry pointer. It can always be upgraded later if we find a need for it... David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 00/18] Introduce automount support in the VFS [ver #4]
Al Viro v...@zeniv.linux.org.uk wrote: d_op-d_automount() may return one of: (a) The vfsmount mounted upon that dentry, in which case pathwalk will move to the root dentry of that vfsmount. -d_automount() must have in some manner mounted this before returning. (b) NULL if something was already mounted there, in which case pathwalk will loop around and recheck the mountings. That makes very little sense as-is. Look: * autofs4 never does (a) * everybody else could replace (a) with (b) just fine - we do (a) only when we'd just mounted new vfsmount on top of path. So (b) would lead to follow_managed() looping over, finding DCACHE_MOUNTED and cheerfully transiting into the root of that vfsmount. Indeed. It's merely an optimisation and not strictly necessary. I suppose that, given the amount of time that's probably spent in performing the mount part of the automount, repeating the check is negligible cost. Now, I'd like to have (a) and (b) distinct, but not in that fashion. Namely, let's take do_add_mount() et.al. into follow_automount(). I presume that people aren't expected to do things like do_move_mount() here, but might they want to do a bind mount? Or do we just say if they want to do something more exotic than do_add_mount(), they have to follow path (b)? Leave autofs4 as in your series; it'll be completely unaffected. But switch all (b) in nfs/cifs/afs over to modified (a). That is, * have vfsmount created as it's done in your series * grab extra reference and put it on chosen list. That'd be done by helper in fs/namespace.c under namespace_sem. Extra ref would make sure that nobody walking the list would decide that it's expirable. It would be simpler, perhaps, to allow d_automount() to return the list also: struct vfsmount *(*d_automount)(struct path *mountpoint, struct list_head **expiry_list_to_use); This pointer can then be passed directly to do_add_mount() and we don't have to worry about having an extra reference or cleaning up the list on error. * schedule whatever expiry activity we currently do. * return vfsmount In follow_automount() we'd see that we have non-NULL and non-ERR_PTR. Then we'd attempt do_add_mount(), without bothering to pass it expiry list. And do the same checks for return value, etc. we currently do in the method instances; just remember that we have an extra vfsmount reference that will need to be dropped and that we'll need to take the sucker off the expiry list in case we decide we don't need it (again, namespace.c helper). As the result, we stop abusing do_add_mount() in there. Moreover, with pending mnt_devname nfs rework we will be able to get rid of passing vfsmount to -d_automount(), AFAICT, which would be nice... :-) David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 00/18] Introduce automount support in the VFS [ver #4]
David Howells dhowe...@redhat.com wrote: It would be simpler, perhaps, to allow d_automount() to return the list also: struct vfsmount *(*d_automount)(struct path *mountpoint, struct list_head **expiry_list_to_use); This pointer can then be passed directly to do_add_mount() and we don't have to worry about having an extra reference or cleaning up the list on error. However, that isn't good enough as the filesystem may also need to start up the time-based expirer, which in the case of AFS, NFS and CIFS doesn't repeat if the list is empty - so if the mounting process gets preempted... David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 09/18] autofs4: Add d_manage() dentry operation [ver #4]
Nick Piggin npig...@gmail.com wrote: On Thu, 2011-01-13 at 21:54 +, David Howells wrote: From: Ian Kent ra...@themaw.net + //spin_lock(dcache_lock); /// JUST DELETE THIS LOCK? + if (!d_mountpoint(dentry) list_empty(dentry-d_subdirs)) { + spin_lock(dentry-d_lock); + if (!(dentry-d_flags DCACHE_MANAGE_TRANSIT) + (dentry-d_flags DCACHE_NEED_AUTOMOUNT)) + __managed_dentry_set_transit(path-dentry); + spin_unlock(dentry-d_lock); + } + //spin_unlock(dcache_lock); In this case I think the dcache_lock needs to be deleted and the d_lock moved out of the if to protect the d_subdirs access. Right. If you follow the vfs-scale-working git branch series of patches leading up to dcache_lock removal, it gives a pretty good template of how to convert old dcache_lock using code to new locking. Although you can also just look at locking in fs/dcache.c and convert code from that. Any time you are dealing with just a *single* dentry, then -d_lock would be enough to replace dcache_lock (it actually protects more than dcache_lock alone did). Does it make sense to leave the lock where it is and repeat the outer test after we've taken the lock? David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 19/18] Unexport do_add_mount() and add in follow_automount(), not -d_automount()
Unexport do_add_mount() and make -d_automount() return the vfsmount to be added rather than calling do_add_mount() itself. follow_automount() will then do the addition. This slightly complicates things as -d_automount() normally wants to add the new vfsmount to an expiration list and start an expiration timer. The problem with that is that the vfsmount will be deleted if it has a refcount of 1 and the timer will not repeat if the expiration list is empty. To this end, we require the vfsmount to be returned from d_automount() with a refcount of (at least) 2. One of these refs will be dropped unconditionally. In addition, follow_automount() must get a 3rd ref around the call to do_add_mount() lest it eat a ref and return an error, leaving the mount we have open to being expired as we would otherwise have only 1 ref on it. This would mean the currently upstream code is buggy for AFS, CIFS and NFS. d_automount() should also add the the vfsmount to the expiration list (by calling mnt_set_expiry()) and start the expiration timer before returning, if this mechanism is to be used. The vfsmount will be unlinked from the expiration list by follow_automount() if do_add_mount() fails. This patch also fixes the call to do_add_mount() for AFS and CIFS to propagate the mount flags from the parent vfsmount. Signed-off-by: David Howells dhowe...@redhat.com --- Documentation/filesystems/vfs.txt | 23 fs/afs/mntpt.c| 25 +- fs/cifs/cifs_dfs_ref.c| 26 +-- fs/internal.h |2 ++ fs/namei.c| 42 +++-- fs/namespace.c| 41 +--- fs/nfs/namespace.c| 24 - include/linux/mount.h |7 +- 8 files changed, 101 insertions(+), 89 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 3c4b2f1..94cf97b 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -933,15 +933,20 @@ struct dentry_operations { dynamic_dname() helper function is provided to take care of this. d_automount: called when an automount dentry is to be traversed (optional). - This should create a new VFS mount record, mount it on the directory - and return the record to the caller. The caller is supplied with a - path parameter giving the automount directory to describe the automount - target and the parent VFS mount record to provide inheritable mount - parameters. NULL should be returned if someone else managed to make - the automount first. If the automount failed, then an error code - should be returned. If -EISDIR is returned, then the directory will - be treated as an ordinary directory and returned to pathwalk to - continue walking. + This should create a new VFS mount record and return the record to the + caller. The caller is supplied with a path parameter giving the + automount directory to describe the automount target and the parent + VFS mount record to provide inheritable mount parameters. NULL should + be returned if someone else managed to make the automount first. If + the vfsmount creation failed, then an error code should be returned. + If -EISDIR is returned, then the directory will be treated as an + ordinary directory and returned to pathwalk to continue walking. + + If a vfsmount is returned, the caller will attempt to mount it on the + mountpoint and will remove the vfsmount from its expiration list in + the case of failure. The vfsmount should be returned with 2 refs on + it to prevent automatic expiration - the caller will clean up the + additional ref. This function is only used if DCACHE_NEED_AUTOMOUNT is set on the dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 0f7dd7a..0d74c2c 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -241,7 +241,6 @@ error_no_devname: struct vfsmount *afs_d_automount(struct path *path) { struct vfsmount *newmnt; - int err; _enter({%s,%s}, path-mnt-mnt_devname, path-dentry-d_name.name); @@ -249,24 +248,12 @@ struct vfsmount *afs_d_automount(struct path *path) if (IS_ERR(newmnt)) return newmnt; - mntget(newmnt); - err = do_add_mount(newmnt, path, MNT_SHRINKABLE, afs_vfsmounts); - switch (err) { - case 0: - schedule_delayed_work(afs_mntpt_expiry_timer, - afs_mntpt_expiry_timeout * HZ); - _leave( = %p {%s}, newmnt, newmnt-mnt_devname); - return newmnt; - case -EBUSY: - /* someone else made a mount here whilst we were busy
Re: [autofs] [PATCH 19/18] Unexport do_add_mount() and add in follow_automount(), not -d_automount()
David Howells dhowe...@redhat.com wrote: This would mean the currently upstream code is buggy for AFS, CIFS and NFS. Actually, no it wouldn't, since do_add_mount() is what adds the mount to the expiration list. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link()
Nick Piggin npig...@gmail.com wrote: So something has gone wrong here. You have documented .d_automount can be called in rcu-walk mode, but it doesn't seem to be the case. Ah. You removed a column and installed a new one, and I didn't notice. Neither d_automount() and d_manage() should be entered in rcu-walk mode since they're both expected to sleep. Btw, should you add a fifth column for d_seq? I should also add a column for namespace_sem as d_manage() may be called with that held (and d_automount() must not be called with that held). David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link()
Nick Piggin npig...@gmail.com wrote: I would still prefer to see a .follow_mount API, and not tie in this automount specific inode detail to what could be a more flexible dentry-only API. The default NULL implementation would do nothing, and follow_automount stuff can be in fs/libfs.c to be used by filesystem, rather than fs/namei.c. Looking further in the patchset at the d_managed thing, that's almost what I'm getting at. But I don't see why any of this stuff has to happen in fs/namei.c. Just call the function from path walk, and provide helpers in libfs or something if there is a lot of common code between autofs4 and others (and leave it autofs specifc when that is the case). Of course, that would be the obvious and naive first approach. So really my question is why did that not work? And can we make it work? You have a strange idea of what is 'obvious and naive'. These are parts of pathwalk, and as such should be in fs/namei.c. I'd rather not expose pathwalking directly to the filesystem, though I acknowledge that sometimes it is necessary to let the filesystem influence it. You need to consider d_automount() and d_manage() separately as they provide two quite different hooks with different properties. Firstly, d_automount(). The following are my points of consideration. (0) You're only allowed to automount on a directory. (1) Automounting does not need to be done when we follow .. to an automount point. (2) Automount points that are mounted upon within the current namespace can just be skipped over. This is the fast path. (3) All the filesystem should need as a parameter to determine what it is allowed to mount is the inode and dentry of the automount point. This holds true for all the things that currently do automounting (AFS, CIFS, NFS, autofs). (4) All the filesystem should need to do is set up a vfsmount struct and publish it or return an indication that there was a collision and the transit should be retried. (5) The filesystem is expected to sleep to achieve the automount, so spinlocks, RCU locks, preemption disablements or interrupt disablements may not be held across this function. (6) The filesystem is expected to need a write lock on namespace_sem at some point, so this must not be held across the call to d_automount(). (7) The filesystem won't necessarily be calling do_add_mount() itself in d_automount() - in autofs's case, the construction is performed by the userspace daemon and then autofs4_d_automount() indicates a collision - so we can't move the do_add_mount() to the caller. Additionally, the filesystem may want to use an expiration list. (8) There needs to be some limitation in place to prevent runaway automounting. The ELOOP detection mechanism can be used for this. Taking these considerations, it shows that a small amount of code can be inserted into pathwalk and used for everything. However, having worked with Ian to try and get autofs4 to work with this, we came up with d_manage() to add in a missing piece. Note that autofs4 also uses d_automount() to build directory structures behind the mountpoint rather than mounting on the mountpoint. In this case, it clears the AUTOMOUNT flag when construction is complete. I've allowed d_automount() to return -EISDIR to follow_automount() to indicate that no mount should be attempted here, and the directory should be given back to pathwalk to treat as a directory. This allows autofs's daemon access to the directory. Having follow_automount() update the path it has been given with the new vfsmount and root dentry is purely an optimisation; we could instead simply return and __follow_mount() will do lookup_mnt() again as it would if a collision is reported. In answer to why I haven't made __follow_mount_rcu() handle automount points, I thought previously I saw a reason why it was unnecessary, but now I'm not so sure. It may be that if there are child objects of this dentry then it will walk onto those rather than automounting - but for some reason it seems still to automount rather than doing that. Secondly, d_manage(). The following are the points of consideration: (1) A filesystem may want to hold up client processes that want to transit from directories in its control during pathwalk - such as when autofs is letting its userspace daemon tear down the stuff mounted on or created behind a directory. (2) A transit may be from a directory to a directory mounted over it, or from a directory to an object (file, dir, etc.) pointed to by an entry in that directoy. (3) The management of dentries in this fashion is a transient affair. (4) The mode in which the filesystem is normally entered for this purpose should be disabled as soon as possible, though it may be reenabled later if needed. (5) When the filesystem is ready it should let the held processes proceed
[autofs] [PATCH 00/18] Introduce automount support in the VFS [ver #4]
Piggin's RCU-based pathwalk changes. [ver #2] - Fixed a EXDEV in patch 6 to be EISDIR. We were previously using EXDEV to indicate we wanted to handle a directory as a directory and not to process it as a mountpoint. - Move some autofs v4 pseudo mount bits into the v4 pseudo direct mount patch [patch 16]. - Move a comment fix to the autofs d_automount() patch [patch 10 - 9]. - Adjust the patch titles of the last three autofs patches. David --- David Howells (9): Allow d_manage() to be used in RCU-walk mode Remove a further kludge from __do_follow_link() Remove the automount through follow_link() kludge code from pathwalk CIFS: Use d_automount() rather than abusing follow_link() NFS: Use d_automount() rather than abusing follow_link() AFS: Use d_automount() rather than abusing follow_link() From: David Howells dhowe...@redhat.com Add a dentry op to allow processes to be held during pathwalk transit Add a dentry op to handle automounting rather than abusing follow_link() Ian Kent (9): autofs4: Bump version autofs4: Add v4 pseudo direct mount support autofs4: Fix wait validation autofs4: Clean up autofs4_free_ino() autofs4: Clean up dentry operations autofs4: Clean up inode operations autofs4: Remove unused code autofs4: Add d_manage() dentry operation autofs4: Add d_automount() dentry operation Documentation/filesystems/Locking |3 Documentation/filesystems/vfs.txt | 38 ++ drivers/staging/autofs/dirhash.c |5 fs/afs/dir.c |1 fs/afs/inode.c|3 fs/afs/internal.h |1 fs/afs/mntpt.c| 47 +-- fs/autofs4/autofs_i.h | 100 - fs/autofs4/dev-ioctl.c|2 fs/autofs4/expire.c | 51 ++- fs/autofs4/inode.c| 28 -- fs/autofs4/root.c | 685 - fs/autofs4/waitq.c| 17 + fs/cifs/cifs_dfs_ref.c| 134 --- fs/cifs/cifsfs.h |6 fs/cifs/dir.c |2 fs/cifs/inode.c |8 fs/dcache.c |5 fs/namei.c| 307 + fs/namespace.c| 14 - fs/nfs/dir.c |4 fs/nfs/inode.c|4 fs/nfs/internal.h |1 fs/nfs/namespace.c| 87 ++--- fs/nfsd/vfs.c |5 fs/stat.c |4 include/linux/auto_fs4.h |2 include/linux/dcache.h| 16 + include/linux/fcntl.h |1 include/linux/fs.h|2 include/linux/namei.h |5 include/linux/nfs_fs.h|1 32 files changed, 894 insertions(+), 695 deletions(-) ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link() [ver #4]
Add a dentry op (d_automount) to handle automounting directories rather than abusing the follow_link() inode operation. The operation is keyed off a new dentry flag (DCACHE_NEED_AUTOMOUNT). This also makes it easier to add an AT_ flag to suppress terminal segment automount during pathwalk and removes the need for the kludge code in the pathwalk algorithm to handle directories with follow_link() semantics. The -d_automount() dentry operation: struct vfsmount *(*d_automount)(struct path *mountpoint); takes a pointer to the directory to be mounted upon, which is expected to provide sufficient data to determine what should be mounted. If successful, it should return the vfsmount struct it creates (which it should also have added to the namespace using do_add_mount() or similar). If there's a collision with another automount attempt, NULL should be returned. If the directory specified by the parameter should be used directly rather than being mounted upon, -EISDIR should be returned. In any other case, an error code should be returned. The -d_automount() operation is called with no locks held and may sleep. At this point the pathwalk algorithm will be in ref-walk mode. Within fs/namei.c itself, a new pathwalk subroutine (follow_automount()) is added to handle mountpoints. It will return -EREMOTE if the automount flag was set, but no d_automount() op was supplied, -ELOOP if we've encountered too many symlinks or mountpoints, -EISDIR if the walk point should be used without mounting and 0 if successful. The path will be updated to point to the mounted filesystem if a successful automount took place. __follow_mount() is replaced by follow_managed() which is more generic (especially with the patch that adds -d_manage()). This handles transits from directories during pathwalk, including automounting and skipping over mountpoints (and holding processes with the next patch). __follow_mount_rcu() will jump out of RCU-walk mode if it encounters an automount point with nothing mounted on it. follow_dotdot*() does not handle automounts as you don't want to trigger them whilst following ... I've also extracted the mount/don't-mount logic from autofs4 and included it here. It makes the mount go ahead anyway if someone calls open() or creat(), tries to traverse the directory, tries to chdir/chroot/etc. into the directory, or sticks a '/' on the end of the pathname. If they do a stat(), however, they'll only trigger the automount if they didn't also say O_NOFOLLOW. I've also added an inode flag (S_AUTOMOUNT) so that filesystems can mark their inodes as automount points. This flag is automatically propagated to the dentry as DCACHE_NEED_AUTOMOUNT by __d_instantiate(). This saves NFS and could save AFS a private flag bit apiece, but is not strictly necessary. It would be preferable to do the propagation in d_set_d_op(), but that doesn't normally have access to the inode. Signed-off-by: David Howells dhowe...@redhat.com Was-Acked-by: Ian Kent ra...@themaw.net --- Documentation/filesystems/Locking |2 Documentation/filesystems/vfs.txt | 14 +++ fs/dcache.c |5 + fs/namei.c| 205 + include/linux/dcache.h|7 + include/linux/fs.h|2 6 files changed, 187 insertions(+), 48 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 977d891..5f0c52a 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -19,6 +19,7 @@ prototypes: void (*d_release)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); + struct vfsmount *(*d_automount)(struct path *path); locking rules: rename_lock -d_lockmay block rcu-walk @@ -29,6 +30,7 @@ d_delete: no yes no no d_release: no no yes no d_iput:no no yes no d_dname: no no no no +d_automount: no no yes no --- inode_operations --- prototypes: diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index fbb324e..992cf74 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -864,6 +864,7 @@ struct dentry_operations { void (*d_release)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); + struct vfsmount *(*d_automount)(struct path *); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -930,6 +931,19 @@ struct dentry_operations { at the end of the buffer
[autofs] [PATCH 03/18] From: David Howells dhowe...@redhat.com [ver #4]
Add an AT_NO_AUTOMOUNT flag to suppress terminal automount Add an AT_NO_AUTOMOUNT flag to suppress terminal automounting of automount point directories. This can be used by fstatat() users to permit the gathering of attributes on an automount point and also prevent mass-automounting of a directory of automount points by ls. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/namei.c|6 ++ fs/stat.c |4 +++- include/linux/fcntl.h |1 + include/linux/namei.h |2 ++ 4 files changed, 12 insertions(+), 1 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e46a56b..249d0f2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -889,6 +889,12 @@ static int follow_automount(struct path *path, unsigned flags, if (!path-dentry-d_op || !path-dentry-d_op-d_automount) return -EREMOTE; + /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT +* and this is the terminal part of the path. +*/ + if ((flags LOOKUP_NO_AUTOMOUNT) !(flags LOOKUP_CONTINUE)) + return -EISDIR; /* we actually want to stop here */ + /* We want to mount if someone is trying to open/create a file of any * type under the mountpoint, wants to traverse through the mountpoint * or wants to open the mounted directory. diff --git a/fs/stat.c b/fs/stat.c index 12e90e2..d5c61cf 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int error = -EINVAL; int lookup_flags = 0; - if ((flag ~AT_SYMLINK_NOFOLLOW) != 0) + if ((flag ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0) goto out; if (!(flag AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; + if (flag AT_NO_AUTOMOUNT) + lookup_flags |= LOOKUP_NO_AUTOMOUNT; error = user_path_at(dfd, filename, lookup_flags, path); if (error) diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index afc00af..a562fa5 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -45,6 +45,7 @@ #define AT_REMOVEDIR 0x200 /* Remove directory instead of unlinking file. */ #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ +#define AT_NO_AUTOMOUNT0x800 /* Suppress terminal automount traversal */ #ifdef __KERNEL__ diff --git a/include/linux/namei.h b/include/linux/namei.h index 8ef2c78..f276d4f 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -45,6 +45,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; * - ending slashes ok even for nonexistent files * - internal there are more path components flag * - dentry cache is untrusted; force a real lookup + * - suppress terminal automount */ #define LOOKUP_FOLLOW 0x0001 #define LOOKUP_DIRECTORY 0x0002 @@ -53,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_PARENT 0x0010 #define LOOKUP_REVAL 0x0020 #define LOOKUP_RCU 0x0040 +#define LOOKUP_NO_AUTOMOUNT0x0080 /* * Intent data */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 09/18] autofs4: Add d_manage() dentry operation [ver #4]
From: Ian Kent ra...@themaw.net This patch required a previous patch to add the -d_automount() dentry operation. Add a function to use the newly defined -d_manage() dentry operation for blocking during mount and expire. Whether the VFS calls the dentry operations d_automount() and d_manage() is controled by the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags. autofs uses the d_automount() operation to callback to user space to request mount operations and the d_manage() operation to block walks into mounts that are under construction or destruction. In order to prevent these functions from being called unnecessarily the DMANAGED_* flags are cleared for cases which would cause this. In the common case the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags are both set for dentrys waiting to be mounted. The DMANAGED_TRANSIT flag is cleared upon successful mount request completion and set during expire runs, both during the dentry expire check, and if selected for expire, is left set until a subsequent successful mount request completes. The exception to this is the so-called rootless multi-mount which has no actual mount at its base. In this case the DMANAGED_AUTOMOUNT flag is cleared upon successful mount request completion as well and set again after a successful expire. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h | 50 - fs/autofs4/expire.c | 51 + fs/autofs4/inode.c|3 + fs/autofs4/root.c | 100 +++-- 4 files changed, 164 insertions(+), 40 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 1ebfe53..7eff538 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -99,7 +99,6 @@ struct autofs_info { }; #define AUTOFS_INF_EXPIRING(10) /* dentry is in the process of expiring */ -#define AUTOFS_INF_MOUNTPOINT (11) /* mountpoint status for direct expire */ #define AUTOFS_INF_PENDING (12) /* dentry pending mount */ struct autofs_wait_queue { @@ -221,6 +220,7 @@ extern const struct file_operations autofs4_root_operations; /* Operations methods */ struct vfsmount *autofs4_d_automount(struct path *); +int autofs4_d_manage(struct path *, bool); /* VFS automount flags management functions */ @@ -248,6 +248,54 @@ static inline void managed_dentry_clear_automount(struct dentry *dentry) spin_unlock(dentry-d_lock); } +static inline void __managed_dentry_set_transit(struct dentry *dentry) +{ + dentry-d_flags |= DCACHE_MANAGE_TRANSIT; +} + +static inline void managed_dentry_set_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_transit(struct dentry *dentry) +{ + dentry-d_flags = ~DCACHE_MANAGE_TRANSIT; +} + +static inline void managed_dentry_clear_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_set_managed(struct dentry *dentry) +{ + dentry-d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT); +} + +static inline void managed_dentry_set_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_managed(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_managed(struct dentry *dentry) +{ + dentry-d_flags = ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT); +} + +static inline void managed_dentry_clear_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_managed(dentry); + spin_unlock(dentry-d_lock); +} + /* Initializing function */ int autofs4_fill_super(struct super_block *, void *, int); diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 0571ec8..3ed79d7 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry, if (ino == NULL) return 0; - /* No point expiring a pending mount */ - if (ino-flags AUTOFS_INF_PENDING) - return 0; - if (!do_now) { /* Too young to die */ if (!timeout || time_after(ino-last_used + timeout, now)) @@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, unsigned long timeout; struct dentry *root = dget(sb-s_root); int do_now = how AUTOFS_EXP_IMMEDIATE; + struct autofs_info *ino; if (!root) return NULL; @@ -291,20 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, timeout = sbi-exp_timeout; spin_lock(sbi-fs_lock); + ino = autofs4_dentry_ino(root); + /* No point expiring a pending mount
[autofs] [PATCH 11/18] autofs4: Clean up inode operations [ver #4]
From: Ian Kent ra...@themaw.net Since the use of -follow_link() has been eliminated there is no need to separate the indirect and direct inode operations. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |3 --- fs/autofs4/inode.c|4 +--- fs/autofs4/root.c | 15 --- 3 files changed, 1 insertions(+), 21 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 8b746f6..c3b0afe 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -204,9 +204,6 @@ void autofs_dev_ioctl_exit(void); extern const struct inode_operations autofs4_symlink_inode_operations; extern const struct inode_operations autofs4_dir_inode_operations; -extern const struct inode_operations autofs4_root_inode_operations; -extern const struct inode_operations autofs4_indirect_root_inode_operations; -extern const struct inode_operations autofs4_direct_root_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 75c1ed8..dac3dc7 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -326,9 +326,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) __managed_dentry_set_managed(root); root_inode-i_fop = autofs4_root_operations; - root_inode-i_op = autofs_type_trigger(sbi-type) ? - autofs4_direct_root_inode_operations : - autofs4_indirect_root_inode_operations; + root_inode-i_op = autofs4_dir_inode_operations; /* Couldn't this be tested earlier? */ if (sbi-max_proto AUTOFS_MIN_PROTO_VERSION || diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 91db5dd..ab391d4 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -56,21 +56,6 @@ const struct file_operations autofs4_dir_operations = { .llseek = dcache_dir_lseek, }; -const 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, -}; - -const struct inode_operations autofs4_direct_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .mkdir = autofs4_dir_mkdir, - .rmdir = autofs4_dir_rmdir, -}; - const struct inode_operations autofs4_dir_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 12/18] autofs4: Clean up dentry operations [ver #4]
From: Ian Kent ra...@themaw.net There are now two distinct dentry operations uses. One for dentrys that trigger mounts and one for dentrys that do not. Rationalize the use of these dentry operations and rename them to reflect their function. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |7 ++- fs/autofs4/inode.c| 12 fs/autofs4/root.c | 36 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index c3b0afe..c28085c 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -206,11 +206,8 @@ extern const struct inode_operations autofs4_symlink_inode_operations; extern const struct inode_operations autofs4_dir_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; - -/* Operations methods */ - -struct vfsmount *autofs4_d_automount(struct path *); -int autofs4_d_manage(struct path *, bool); +extern const struct dentry_operations autofs4_dentry_operations; +extern const struct dentry_operations autofs4_mount_dentry_operations; /* VFS automount flags management functions */ diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index dac3dc7..427c357 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -251,12 +251,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) return ino; } -static const struct dentry_operations autofs4_sb_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - int autofs4_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; @@ -311,7 +305,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_iput; pipe = NULL; - d_set_d_op(root, autofs4_sb_dentry_operations); + d_set_d_op(root, autofs4_dentry_operations); root-d_fsdata = ino; /* Can this call block? */ @@ -322,8 +316,10 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_dput; } - if (autofs_type_trigger(sbi-type)) + if (autofs_type_trigger(sbi-type)) { + d_set_d_op(root, autofs4_mount_dentry_operations); __managed_dentry_set_managed(root); + } root_inode-i_fop = autofs4_root_operations; root_inode-i_op = autofs4_dir_inode_operations; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index ab391d4..b3ab9e9 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -35,6 +35,8 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); #endif static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); +static struct vfsmount *autofs4_d_automount(struct path *); +static int autofs4_d_manage(struct path *, bool); const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, @@ -64,6 +66,18 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; +/* For dentries that don't initiate mounting */ +const struct dentry_operations autofs4_dentry_operations = { + .d_release = autofs4_dentry_release, +}; + +/* For dentries that do initiate mounting */ +const struct dentry_operations autofs4_mount_dentry_operations = { + .d_automount= autofs4_d_automount, + .d_manage = autofs4_d_manage, + .d_release = autofs4_dentry_release, +}; + static void autofs4_add_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -158,18 +172,6 @@ void autofs4_dentry_release(struct dentry *de) } } -/* For dentries of directories in the root dir */ -static const struct dentry_operations autofs4_root_dentry_operations = { - .d_release = autofs4_dentry_release, -}; - -/* For other dentries */ -static const struct dentry_operations autofs4_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - static struct dentry *autofs4_lookup_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -337,7 +339,7 @@ static struct dentry *autofs4_mountpoint_changed(struct path *path) return path-dentry; } -struct vfsmount *autofs4_d_automount(struct path *path) +static struct vfsmount *autofs4_d_automount(struct path *path) { struct dentry *dentry = path-dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -506,7 +508,7 @@ static struct dentry *autofs4_lookup
[autofs] [PATCH 13/18] autofs4: Clean up autofs4_free_ino() [ver #4]
From: Ian Kent ra...@themaw.net When this function is called the local reference count does't need to be updated since the dentry is going away and dput definitely must not be called here. Also the autofs info struct field inode isn't used so remove it. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/inode.c | 13 - fs/autofs4/root.c |9 - 2 files changed, 0 insertions(+), 22 deletions(-) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 427c357..3ecd2e2 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, if (!reinit) { ino-flags = 0; - ino-inode = NULL; ino-dentry = NULL; ino-size = 0; INIT_LIST_HEAD(ino-active); @@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, 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) { - 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) @@ -390,7 +378,6 @@ struct inode *autofs4_get_inode(struct super_block *sb, if (inode == NULL) return NULL; - inf-inode = inode; inode-i_mode = inf-mode; if (sb-s_root) { inode-i_uid = sb-s_root-d_inode-i_uid; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index b3ab9e9..51a4c2a 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -151,11 +151,8 @@ void autofs4_dentry_release(struct dentry *de) DPRINTK(releasing %p, de); inf = autofs4_dentry_ino(de); - 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)) @@ -164,10 +161,6 @@ void autofs4_dentry_release(struct dentry *de) list_del(inf-expiring); spin_unlock(sbi-lookup_lock); } - - inf-dentry = NULL; - inf-inode = NULL; - autofs4_free_ino(inf); } } @@ -588,7 +581,6 @@ static int autofs4_dir_symlink(struct inode *dir, 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; @@ -718,7 +710,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) p_ino = autofs4_dentry_ino(dentry-d_parent); if (p_ino dentry-d_parent != dentry) atomic_inc(p_ino-count); - ino-inode = inode; inc_nlink(dir); dir-i_mtime = CURRENT_TIME; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 14/18] autofs4: Fix wait validation [ver #4]
From: Ian Kent ra...@themaw.net It is possible for the check in wait.c:validate_request() to return an incorrect result if the dentry that was mounted upon has changed during the callback. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/waitq.c | 17 - 1 files changed, 16 insertions(+), 1 deletions(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index c5f8459..5601005 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait, * completed while we waited on the mutex ... */ if (notify == NFY_MOUNT) { + struct dentry *new = NULL; + int valid = 1; + /* * If the dentry was successfully mounted while we slept * on the wait queue mutex we can return success. If it @@ -316,8 +319,20 @@ static int validate_request(struct autofs_wait_queue **wait, * a multi-mount with no mount at it's base) we can * continue on and create a new request. */ + if (!IS_ROOT(dentry)) { + if (dentry-d_inode d_unhashed(dentry)) { + struct dentry *parent = dentry-d_parent; + new = d_lookup(parent, dentry-d_name); + if (new) + dentry = new; + } + } if (have_submounts(dentry)) - return 0; + valid = 0; + + if (new) + dput(new); + return valid; } return 1; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 16/18] autofs4: Bump version [ver #4]
From: Ian Kent ra...@themaw.net Increase the autofs module sub-version so we can tell what kernel implementation is being used from user space debug logging. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- include/linux/auto_fs4.h |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 8b49ac4..e02982f 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -24,7 +24,7 @@ #define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MAX_PROTO_VERSION 5 -#define AUTOFS_PROTO_SUBVERSION1 +#define AUTOFS_PROTO_SUBVERSION2 /* Mask for expire behaviour */ #define AUTOFS_EXP_IMMEDIATE 1 ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 18/18] Allow d_manage() to be used in RCU-walk mode [ver #4]
Allow d_manage() to be called from pathwalk when it is in RCU-walk mode as well as when it is in Ref-walk mode. This permits __follow_mount_rcu() to call d_manage() directly. d_manage() needs a parameter to indicate that it is in RCU-walk mode as it isn't allowed to sleep if in that mode (but should return -ECHILD instead). autofs4_d_manage() can then be set to retain RCU-walk mode if the daemon accesses it and otherwise request dropping back to ref-walk mode. Signed-off-by: David Howells dhowe...@redhat.com --- Documentation/filesystems/Locking |2 +- Documentation/filesystems/vfs.txt |7 ++- fs/autofs4/root.c |8 ++-- fs/namei.c| 14 +++--- include/linux/dcache.h|2 +- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 621ee98..b3b0ac4 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -31,7 +31,7 @@ d_release:no no yes no d_iput:no no yes no d_dname: no no no no d_automount: no no yes no -d_manage: no no yes no +d_manage: no no yes (ref-walk) maybe --- inode_operations --- prototypes: diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 3d68c2e..9bc28c2f 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -865,7 +865,7 @@ struct dentry_operations { void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); - int (*d_manage)(struct path *, bool); + int (*d_manage)(struct path *, bool, bool); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -960,6 +960,11 @@ struct dentry_operations { held by the caller and the function should not initiate any mounts or unmounts that it will then wait for. + If the 'rcu_walk' parameter is true, then the caller is doing a + pathwalk in RCU-walk mode. Sleeping is not permitted in this mode, + and the caller can be asked to leave it and call again by returing + -ECHILD. + This function is only used if DCACHE_MANAGE_TRANSIT is set on the dentry being transited from. diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 752693f..c46d6fb 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -36,7 +36,7 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static struct vfsmount *autofs4_d_automount(struct path *); -static int autofs4_d_manage(struct path *, bool); +static int autofs4_d_manage(struct path *, bool, bool); const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, @@ -454,7 +454,7 @@ done: return NULL; } -int autofs4_d_manage(struct path *path, bool mounting_here) +int autofs4_d_manage(struct path *path, bool mounting_here, bool rcu_walk) { struct dentry *dentry = path-dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -469,6 +469,10 @@ int autofs4_d_manage(struct path *path, bool mounting_here) return 0; } + /* We need to sleep, so we need pathwalk to be in ref-mode */ + if (rcu_walk) + return -ECHILD; + /* Wait for pending expires */ do_expire_wait(dentry); diff --git a/fs/namei.c b/fs/namei.c index c983c92..5c15daa 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -967,7 +967,7 @@ static int follow_managed(struct path *path, unsigned flags) if (managed DCACHE_MANAGE_TRANSIT) { BUG_ON(!path-dentry-d_op); BUG_ON(!path-dentry-d_op-d_manage); - ret = path-dentry-d_op-d_manage(path, false); + ret = path-dentry-d_op-d_manage(path, false, false); if (ret 0) return ret == -EISDIR ? 0 : ret; } @@ -1028,13 +1028,12 @@ int follow_down_one(struct path *path) static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, struct inode **inode, bool reverse_transit) { - unsigned abort_mask = - reverse_transit ? 0 : DCACHE_MANAGE_TRANSIT; - while (d_mountpoint(path-dentry)) { struct vfsmount *mounted; - if (path-dentry-d_flags abort_mask
Re: [autofs] [PATCH 07/18] Add more dentry flags for special function directories [UPDATE]
Here's an updated patch 7. d_set_d_op() can't be used to set DCACHE_NEED_AUTOMOUNT as d_inode is not set at that point. It has to be done in __d_instantiate() at the latest. David --- From: David Howells dhowe...@redhat.com Subject: [PATCH] Add more dentry flags for special function directories Add more flags to the d_flags in struct dentry for special function directories such as mountpoints and autofs substructures. The relevant flags are: (*) DCACHE_MOUNTED. (Already exists). Indicates that this dentry has things mounted upon it. (*) DCACHE_NEED_AUTOMOUNT. This is a reflection of the S_AUTOMOUNT inode flag. This is reflected by __d_instantiate() and similar. follow_automount() is now keyed off of the dcache flag rather than being keyed off S_AUTOMOUNT directly. Possibly S_AUTOMOUNT should shift to the dentry entirely. This may also be tweaked live (such as by the autofs4 filesystem) to alter the effect. (*) DCACHE_MANAGE_TRANSIT. This is an indicator that the filesystem that owns the dentry wants to manage processes transiting away from that dentry. If this is set on a dentry, then a new dentry op: int (*d_manage)(struct path *); is invoked. This is allowed to sleep and is allowed to return an error. This allows autofs to hold non-Oz-mode processes here without any filesystem locks being held. __follow_mount() is replaced by managed_dentry() which now handles transit to a mountpoint's root dentry, automount points and points that the filesystem wants to manage. == WHAT THIS MEANS FOR AUTOFS == autofs currently uses the lookup() inode op and the d_revalidate() dentry op to trigger the automounting of indirect mounts, and both of these can be called with i_mutex held. autofs knows that the i_mutex will be held by the caller in lookup(), and so can drop it before invoking the daemon - but this isn't so for d_revalidate(), since the lock is only held on _some_ of the code paths that call it. This means that autofs can't risk dropping i_mutex from its d_revalidate() function before it calls the daemon. The bug could manifest itself as, for example, a process that's trying to validate an automount dentry that gets made to wait because that dentry is expired and needs cleaning up: mkdir S 8014e05a 0 32580 24956 Call Trace: [885371fd] :autofs4:autofs4_wait+0x674/0x897 [80127f7d] avc_has_perm+0x46/0x58 [8009fdcf] autoremove_wake_function+0x0/0x2e [88537be6] :autofs4:autofs4_expire_wait+0x41/0x6b [88535cfc] :autofs4:autofs4_revalidate+0x91/0x149 [80036d96] __lookup_hash+0xa0/0x12f [80057a2f] lookup_create+0x46/0x80 [800e6e31] sys_mkdirat+0x56/0xe4 versus the automount daemon which wants to remove that dentry, but can't because the normal process is holding the i_mutex lock: automount D 8014e05a 0 32581 1 32561 Call Trace: [80063c3f] __mutex_lock_slowpath+0x60/0x9b [8000ccf1] do_path_lookup+0x2ca/0x2f1 [80063c89] .text.lock.mutex+0xf/0x14 [800e6d55] do_rmdir+0x77/0xde [8005d229] tracesys+0x71/0xe0 [8005d28d] tracesys+0xd5/0xe0 which means that the system is deadlocked. This patch allows autofs to hold up normal processes whilst the daemon goes ahead and does things to the dentry tree behind the automouter point without risking a deadlock as no locks are held in d_manage() or d_automount(). Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- Documentation/filesystems/vfs.txt | 13 +++ fs/dcache.c |5 + fs/namei.c| 153 ++--- include/linux/dcache.h| 13 ++- 4 files changed, 132 insertions(+), 52 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index bb8d277..99f0127 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -865,6 +865,7 @@ struct dentry_operations { void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); + int (*d_manage)(struct path *); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -940,8 +941,16 @@ struct dentry_operations { the automount first. If the automount failed, then an error code should be returned. - This function is only used if S_AUTOMOUNT is set on the inode to which - the dentry refers. + This function is only used if DMANAGED_AUTOMOUNT is set on the dentry. + This is set by d_add
Re: [autofs] [PATCH 00/18] Introduce automount support in the VFS
I've updated: http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-2.6-automount.git;a=summary to Linus's latest GIT tree with my latest patches on top. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 10/18] autofs4: Add d_manage() dentry operation
From: Ian Kent ra...@themaw.net This patch required a previous patch to add the -d_automount() dentry operation. Add a function to use the newly defined -d_manage() dentry operation for blocking during mount and expire. Whether the VFS calls the dentry operations d_automount() and d_manage() is controled by the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags. autofs uses the d_automount() operation to callback to user space to request mount operations and the d_manage() operation to block walks into mounts that are under construction or destruction. In order to prevent these functions from being called unnecessarily the DMANAGED_* flags are cleared for cases which would cause this. In the common case the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags are both set for dentrys waiting to be mounted. The DMANAGED_TRANSIT flag is cleared upon successful mount request completion and set during expire runs, both during the dentry expire check, and if selected for expire, is left set until a subsequent successful mount request completes. The exception to this is the so-called rootless multi-mount which has no actual mount at its base. In this case the DMANAGED_AUTOMOUNT flag is cleared upon successful mount request completion as well and set again after a successful expire. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h | 50 - fs/autofs4/expire.c | 51 + fs/autofs4/inode.c|3 + fs/autofs4/root.c | 100 +++-- 4 files changed, 164 insertions(+), 40 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 1ebfe53..7eff538 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -99,7 +99,6 @@ struct autofs_info { }; #define AUTOFS_INF_EXPIRING(10) /* dentry is in the process of expiring */ -#define AUTOFS_INF_MOUNTPOINT (11) /* mountpoint status for direct expire */ #define AUTOFS_INF_PENDING (12) /* dentry pending mount */ struct autofs_wait_queue { @@ -221,6 +220,7 @@ extern const struct file_operations autofs4_root_operations; /* Operations methods */ struct vfsmount *autofs4_d_automount(struct path *); +int autofs4_d_manage(struct path *, bool); /* VFS automount flags management functions */ @@ -248,6 +248,54 @@ static inline void managed_dentry_clear_automount(struct dentry *dentry) spin_unlock(dentry-d_lock); } +static inline void __managed_dentry_set_transit(struct dentry *dentry) +{ + dentry-d_flags |= DCACHE_MANAGE_TRANSIT; +} + +static inline void managed_dentry_set_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_transit(struct dentry *dentry) +{ + dentry-d_flags = ~DCACHE_MANAGE_TRANSIT; +} + +static inline void managed_dentry_clear_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_set_managed(struct dentry *dentry) +{ + dentry-d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT); +} + +static inline void managed_dentry_set_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_managed(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_managed(struct dentry *dentry) +{ + dentry-d_flags = ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT); +} + +static inline void managed_dentry_clear_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_managed(dentry); + spin_unlock(dentry-d_lock); +} + /* Initializing function */ int autofs4_fill_super(struct super_block *, void *, int); diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 0571ec8..3ed79d7 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry, if (ino == NULL) return 0; - /* No point expiring a pending mount */ - if (ino-flags AUTOFS_INF_PENDING) - return 0; - if (!do_now) { /* Too young to die */ if (!timeout || time_after(ino-last_used + timeout, now)) @@ -283,6 +279,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, unsigned long timeout; struct dentry *root = dget(sb-s_root); int do_now = how AUTOFS_EXP_IMMEDIATE; + struct autofs_info *ino; if (!root) return NULL; @@ -291,20 +288,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, timeout = sbi-exp_timeout; spin_lock(sbi-fs_lock); + ino = autofs4_dentry_ino(root); + /* No point expiring a pending mount
[autofs] [PATCH 15/18] autofs4: Fix wait validation
From: Ian Kent ra...@themaw.net It is possible for the check in wait.c:validate_request() to return an incorrect result if the dentry that was mounted upon has changed during the callback. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/waitq.c | 17 - 1 files changed, 16 insertions(+), 1 deletions(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index c5f8459..5601005 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -309,6 +309,9 @@ static int validate_request(struct autofs_wait_queue **wait, * completed while we waited on the mutex ... */ if (notify == NFY_MOUNT) { + struct dentry *new = NULL; + int valid = 1; + /* * If the dentry was successfully mounted while we slept * on the wait queue mutex we can return success. If it @@ -316,8 +319,20 @@ static int validate_request(struct autofs_wait_queue **wait, * a multi-mount with no mount at it's base) we can * continue on and create a new request. */ + if (!IS_ROOT(dentry)) { + if (dentry-d_inode d_unhashed(dentry)) { + struct dentry *parent = dentry-d_parent; + new = d_lookup(parent, dentry-d_name); + if (new) + dentry = new; + } + } if (have_submounts(dentry)) - return 0; + valid = 0; + + if (new) + dput(new); + return valid; } return 1; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 16/18] autofs4: Add v4 pseudo direct mount support
From: Ian Kent ra...@themaw.net Version 4 of autofs provides a pseudo direct mount implementation that relies on directories at the leaves of a directory tree under an indirect mount to trigger mounts. This patch adds support for that functionality. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/root.c | 60 + 1 files changed, 60 insertions(+), 0 deletions(-) diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 51a4c2a..752693f 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -635,6 +635,60 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) return 0; } +/* + * Version 4 of autofs provides a pseudo direct mount implementation + * that relies on directories at the leaves of a directory tree under + * an indirect mount to trigger mounts. To allow for this we need to + * set the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags on the leaves + * of the directory tree. There is no need to clear the automount flag + * following a mount or restore it after an expire because these mounts + * are always covered. However, it is neccessary to ensure that these + * flags are clear on non-empty directories to avoid unnecessary calls + * during path walks. + */ +static void autofs_set_leaf_automount_flags(struct dentry *dentry) +{ + struct dentry *parent; + + /* root and dentrys in the root are already handled */ + if (IS_ROOT(dentry-d_parent)) + return; + + managed_dentry_set_managed(dentry); + + parent = dentry-d_parent; + /* only consider parents below dentrys in the root */ + if (IS_ROOT(parent-d_parent)) + return; + managed_dentry_clear_managed(parent); + return; +} + +static void autofs_clear_leaf_automount_flags(struct dentry *dentry) +{ + struct list_head *d_child; + struct dentry *parent; + + /* flags for dentrys in the root are handled elsewhere */ + if (IS_ROOT(dentry-d_parent)) + return; + + managed_dentry_clear_managed(dentry); + + parent = dentry-d_parent; + /* only consider parents below dentrys in the root */ + if (IS_ROOT(parent-d_parent)) + return; + d_child = dentry-d_u.d_child; + //spin_lock(dcache_lock); // JUST DELETE THIS LOCK? + /* Set parent managed if it's becoming empty */ + if (d_child-next == parent-d_subdirs + d_child-prev == parent-d_subdirs) + managed_dentry_set_managed(parent); + //spin_unlock(dcache_lock); + return; +} + static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dir-i_sb); @@ -662,6 +716,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) spin_unlock(dentry-d_lock); spin_unlock(autofs4_lock); + if (sbi-version 5) + autofs_clear_leaf_automount_flags(dentry); + if (atomic_dec_and_test(ino-count)) { p_ino = autofs4_dentry_ino(dentry-d_parent); if (p_ino dentry-d_parent != dentry) @@ -704,6 +761,9 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) } d_add(dentry, inode); + if (sbi-version 5) + autofs_set_leaf_automount_flags(dentry); + dentry-d_fsdata = ino; ino-dentry = dget(dentry); atomic_inc(ino-count); ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 11/18] autofs4: Remove unused code
From: Ian Kent ra...@themaw.net Remove code that is not used due to the use of -d_automount() and -d_manage(). Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |7 - fs/autofs4/root.c | 243 - 2 files changed, 0 insertions(+), 250 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 7eff538..8b746f6 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -175,13 +175,6 @@ static inline int autofs4_ispending(struct dentry *dentry) return 0; } -static inline void autofs4_copy_atime(struct file *src, struct file *dst) -{ - dst-f_path.dentry-d_inode-i_atime = - src-f_path.dentry-d_inode-i_atime; - return; -} - struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *); void autofs4_free_ino(struct autofs_info *); diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index b2cc422..91db5dd 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -36,9 +36,6 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,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, @@ -114,14 +111,6 @@ static void autofs4_del_active(struct dentry *dentry) return; } -static unsigned int autofs4_need_mount(unsigned int flags) -{ - unsigned int res = 0; - if (flags (TRIGGER_FLAGS | TRIGGER_INTENTS)) - res = 1; - return res; -} - static int autofs4_dir_open(struct inode *inode, struct file *file) { struct dentry *dentry = file-f_path.dentry; @@ -156,238 +145,6 @@ out: 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; - - 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 -*/ - 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); - - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { - spin_lock(sbi-fs_lock); - ino-flags = ~AUTOFS_INF_PENDING; - spin_unlock(sbi-fs_lock); - return status; - } else if (status) { - /* Return a negative dentry, but leave it pending */ - return status; - } - /* Trigger mount for path component or follow link */ - } else if (ino-flags AUTOFS_INF_PENDING || - autofs4_need_mount(flags)) { - DPRINTK(waiting for mount name=%.*s, - dentry-d_name.len, dentry-d_name.name); - - spin_lock(sbi-fs_lock); - ino-flags |= AUTOFS_INF_PENDING; - spin_unlock(sbi-fs_lock); - status = autofs4_wait(sbi, dentry, NFY_MOUNT); - - DPRINTK(mount done status=%d, status); - - if (status) { - spin_lock(sbi-fs_lock); - ino-flags = ~AUTOFS_INF_PENDING; - spin_unlock(sbi-fs_lock); - return status; - } - } - - /* Initialize expiry counter after successful mount */ - ino-last_used = jiffies; - - spin_lock(sbi-fs_lock); - ino-flags = ~AUTOFS_INF_PENDING; - spin_unlock(sbi-fs_lock); - - 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 break out of follow_down_one() at the autofs mount trigger
[autofs] [PATCH 14/18] autofs4: Clean up autofs4_free_ino()
From: Ian Kent ra...@themaw.net When this function is called the local reference count does't need to be updated since the dentry is going away and dput definitely must not be called here. Also the autofs info struct field inode isn't used so remove it. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/inode.c | 13 - fs/autofs4/root.c |9 - 2 files changed, 0 insertions(+), 22 deletions(-) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 427c357..3ecd2e2 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, if (!reinit) { ino-flags = 0; - ino-inode = NULL; ino-dentry = NULL; ino-size = 0; INIT_LIST_HEAD(ino-active); @@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, 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) { - 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) @@ -390,7 +378,6 @@ struct inode *autofs4_get_inode(struct super_block *sb, if (inode == NULL) return NULL; - inf-inode = inode; inode-i_mode = inf-mode; if (sb-s_root) { inode-i_uid = sb-s_root-d_inode-i_uid; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index b3ab9e9..51a4c2a 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -151,11 +151,8 @@ void autofs4_dentry_release(struct dentry *de) DPRINTK(releasing %p, de); inf = autofs4_dentry_ino(de); - 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)) @@ -164,10 +161,6 @@ void autofs4_dentry_release(struct dentry *de) list_del(inf-expiring); spin_unlock(sbi-lookup_lock); } - - inf-dentry = NULL; - inf-inode = NULL; - autofs4_free_ino(inf); } } @@ -588,7 +581,6 @@ static int autofs4_dir_symlink(struct inode *dir, 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; @@ -718,7 +710,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) p_ino = autofs4_dentry_ino(dentry-d_parent); if (p_ino dentry-d_parent != dentry) atomic_inc(p_ino-count); - ino-inode = inode; inc_nlink(dir); dir-i_mtime = CURRENT_TIME; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 03/18] NFS: Use d_automount() rather than abusing follow_link()
Make NFS use the new d_automount() dentry operation rather than abusing follow_link() on directories. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Trond Myklebust trond.mykleb...@netapp.com Acked-by: Ian Kent ra...@themaw.net --- fs/nfs/dir.c |4 ++ fs/nfs/inode.c |4 +- fs/nfs/internal.h |1 + fs/nfs/namespace.c | 87 ++-- include/linux/nfs_fs.h |1 - 5 files changed, 46 insertions(+), 51 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d33da53..959927c 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -970,7 +970,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) { struct nfs_server *server = NFS_SERVER(inode); - if (test_bit(NFS_INO_MOUNTPOINT, NFS_I(inode)-flags)) + if (IS_AUTOMOUNT(inode)) return 0; if (nd != NULL) { /* VFS wants an on-the-wire revalidation */ @@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = { .d_revalidate = nfs_lookup_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput, + .d_automount= nfs_d_automount, }; static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) @@ -1248,6 +1249,7 @@ const struct dentry_operations nfs4_dentry_operations = { .d_revalidate = nfs_open_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput, + .d_automount= nfs_d_automount, }; /* diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 017daa3..6ace134 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -300,7 +300,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) else inode-i_op = nfs_mountpoint_inode_operations; inode-i_fop = NULL; - set_bit(NFS_INO_MOUNTPOINT, nfsi-flags); + inode-i_flags |= S_AUTOMOUNT; } } else if (S_ISLNK(inode-i_mode)) inode-i_op = nfs_symlink_inode_operations; @@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) /* Update the fsid? */ if (S_ISDIR(inode-i_mode) (fattr-valid NFS_ATTR_FATTR_FSID) !nfs_fsid_equal(server-fsid, fattr-fsid) - !test_bit(NFS_INO_MOUNTPOINT, nfsi-flags)) + !IS_AUTOMOUNT(inode)) server-fsid = fattr-fsid; /* diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e6356b7..66c3dc4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -245,6 +245,7 @@ extern char *nfs_path(const char *base, const struct dentry *droot, const struct dentry *dentry, char *buffer, ssize_t buflen); +extern struct vfsmount *nfs_d_automount(struct path *path); /* getroot.c */ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 74aaf39..f3fbb1b 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -97,9 +97,8 @@ Elong: } /* - * nfs_follow_mountpoint - handle crossing a mountpoint on the server - * @dentry - dentry of mountpoint - * @nd - nameidata info + * nfs_d_automount - Handle crossing a mountpoint on the server + * @path - The mountpoint * * When we encounter a mountpoint on the server, we want to set up * a mountpoint on the client too, to prevent inode numbers from @@ -109,87 +108,81 @@ Elong: * situation, and that different filesystems may want to use * different security flavours. */ -static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +struct vfsmount *nfs_d_automount(struct path *path) { struct vfsmount *mnt; - struct nfs_server *server = NFS_SERVER(dentry-d_inode); + struct nfs_server *server = NFS_SERVER(path-dentry-d_inode); struct dentry *parent; struct nfs_fh *fh = NULL; struct nfs_fattr *fattr = NULL; int err; - dprintk(-- nfs_follow_mountpoint()\n); + dprintk(-- nfs_d_automount()\n); - err = -ESTALE; - if (IS_ROOT(dentry)) - goto out_err; + mnt = ERR_PTR(-ESTALE); + if (IS_ROOT(path-dentry)) + goto out_nofree; - err = -ENOMEM; + mnt = ERR_PTR(-ENOMEM); fh = nfs_alloc_fhandle(); fattr = nfs_alloc_fattr(); if (fh == NULL || fattr == NULL) - goto out_err; + goto out; dprintk(%s: enter\n, __func__); - dput(nd-path.dentry); - nd-path.dentry = dget(dentry); - /* Look it up again */ - parent = dget_parent(nd-path.dentry); + /* Look
[autofs] [PATCH 01/18] Add a dentry op to handle automounting rather than abusing follow_link()
Add a dentry op (d_automount) to handle automounting directories rather than abusing the follow_link() inode operation. The operation is keyed off a new inode flag (S_AUTOMOUNT). This makes it easier to add an AT_ flag to suppress terminal segment automount during pathwalk. It should also remove the need for the kludge code in the pathwalk algorithm to handle directories with follow_link() semantics. A new pathwalk subroutine, follow_automount() is added to handle mountpoints. It will return -EREMOTE if the S_AUTOMOUNT was set, but no d_automount() op was supplied, -ELOOP if we've encountered too many symlinks or mountpoints, -EISDIR if the walk point should be used without mounting and 0 if successful. path will be updated if an automount took place to point to the mounted filesystem. I've only changed __follow_mount() to handle call follow_automount(), but it might be necessary to change follow_mount() too. The latter is only called from follow_dotdot(), but any automounts on .. should be pinned whilst we're using a child of it. I've also extracted the mount/don't-mount logic from autofs4 and included it here. It makes the mount go ahead anyway if someone calls open() or creat(), tries to traverse the directory, tries to chdir/chroot/etc. into the directory, or sticks a '/' on the end of the pathname. If they do a stat(), however, they'll only trigger the automount if they didn't also say O_NOFOLLOW. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- Documentation/filesystems/Locking |2 + Documentation/filesystems/vfs.txt | 13 fs/namei.c| 120 - include/linux/dcache.h|4 + include/linux/fs.h|2 + 5 files changed, 111 insertions(+), 30 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 977d891..7ebe42d 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -19,6 +19,7 @@ prototypes: void (*d_release)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); + struct vfsmount *(*d_automount)(struct path *path); locking rules: rename_lock -d_lockmay block rcu-walk @@ -29,6 +30,7 @@ d_delete: no yes no no d_release: no no yes no d_iput:no no yes no d_dname: no no no no +d_automount: no no no yes --- inode_operations --- prototypes: diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index fbb324e..bb8d277 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -864,6 +864,7 @@ struct dentry_operations { void (*d_release)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); + struct vfsmount *(*d_automount)(struct path *); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -930,6 +931,18 @@ struct dentry_operations { at the end of the buffer, and returns a pointer to the first char. dynamic_dname() helper function is provided to take care of this. + d_automount: called when an automount dentry is to be traversed (optional). + This should create a new VFS mount record, mount it on the directory + and return the record to the caller. The caller is supplied with a + path parameter giving the automount directory to describe the automount + target and the parent VFS mount record to provide inheritable mount + parameters. NULL should be returned if someone else managed to make + the automount first. If the automount failed, then an error code + should be returned. + + This function is only used if S_AUTOMOUNT is set on the inode to which + the dentry refers. + Example : static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) diff --git a/fs/namei.c b/fs/namei.c index 24ece10..159da29 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -877,38 +877,84 @@ int follow_up(struct path *path) } /* - * serialization is taken care of in namespace.c + * Perform an automount + * - return -EISDIR to tell __follow_mount() to stop and return the path we + * were called with. */ -static void __follow_mount_rcu(struct nameidata *nd, struct path *path, - struct inode **inode) +static int follow_automount(struct path *path, unsigned flags, + bool *need_mntput) { - while (d_mountpoint(path-dentry)) { - struct
[autofs] [PATCH 08/18] Make follow_down() handle d_manage()
The previous patch (that adds d_manage()) offers autofs the opportunity to block processes whilst it is rearranging its dentry tree, but only covers cases where managed_dentry() is called. Some places call follow_down(), which would allow processes to bypass autofs's attempts to block them. Make follow_down() handle managed dentries. Do this by renaming follow_down() to follow_down_one() and instituting a new follow_down(). follow_down_one() is then only used where a call to d_manage() is not needed. follow_down() then incorporates the loop from its remaining callers to follow down through all mounted filesystems at that point. Before each mountpoint is transited and if requested by the filesystem, d_manage() is called to hold or reject that transit. The callers of follow_down() must then handle a possible error condition. follow_down() is given a parameter to say whether someone is trying to mount on that point (and holding namespace_sem). This is now passed on to d_manage(). The filesystem may reject this request by returning an error from d_manage(). Furthermore, d_manage() may end follow_down() processing early by returning -EISDIR to indicate it wants the dentry to be dealt with as an ordinary directory, not a mountpoint. This permits autofs to let its daemon see the underlying dentry. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- drivers/staging/autofs/dirhash.c |5 +-- fs/autofs4/autofs_i.h| 13 - fs/autofs4/dev-ioctl.c |2 + fs/autofs4/expire.c |2 + fs/autofs4/root.c| 11 +++ fs/namei.c | 58 -- fs/namespace.c | 14 + fs/nfsd/vfs.c|5 ++- include/linux/dcache.h |7 - include/linux/namei.h|3 +- 10 files changed, 83 insertions(+), 37 deletions(-) diff --git a/drivers/staging/autofs/dirhash.c b/drivers/staging/autofs/dirhash.c index d3f42c8..a08bd73 100644 --- a/drivers/staging/autofs/dirhash.c +++ b/drivers/staging/autofs/dirhash.c @@ -88,14 +88,13 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, } path.mnt = mnt; path_get(path); - if (!follow_down(path)) { + if (!follow_down_one(path)) { path_put(path); DPRINTK((autofs: not expirable\ (not a mounted directory): %s\n, ent-name)); continue; } - while (d_mountpoint(path.dentry) follow_down(path)) - ; + follow_down(path, false); // TODO: need to check error umount_ok = may_umount(path.mnt); path_put(path); diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 0fffe1c..eb67953 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -229,19 +229,6 @@ 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 path *path) -{ - int res = 0; - - while (d_mountpoint(path-dentry)) { - int followed = follow_down(path); - 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); diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index eff9a41..1442da4 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, err = have_submounts(path.dentry); - if (follow_down(path)) + if (follow_down_one(path)) magic = path.mnt-mnt_sb-s_magic; } diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index cc1d013..6a930b9 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -56,7 +56,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) path_get(path); - if (!follow_down(path)) + if (!follow_down_one(path)) goto done; if (is_autofs4_dentry(path.dentry)) { diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 651e4ef..2022563 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -234,7 +234,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) nd-flags); /* * For an expire of a covered direct or offset mount we need -* to break out of follow_down() at the autofs mount trigger +* to break out of follow_down_one() at the autofs mount trigger * (d_mounted--), so
[autofs] [PATCH 02/18] AFS: Use d_automount() rather than abusing follow_link()
Make AFS use the new d_automount() dentry operation rather than abusing follow_link() on directories. Signed-off-by: David Howells dhowe...@redhat.com --- fs/afs/dir.c |1 + fs/afs/inode.c|3 ++- fs/afs/internal.h |1 + fs/afs/mntpt.c| 47 +++ 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 34a3263..70f07e3 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -66,6 +66,7 @@ static const struct dentry_operations afs_fs_dentry_operations = { .d_revalidate = afs_d_revalidate, .d_delete = afs_d_delete, .d_release = afs_d_release, + .d_automount= afs_d_automount, }; #define AFS_DIR_HASHTBL_SIZE 128 diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 0747339..db66c52 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -184,7 +184,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, inode-i_generation = 0; set_bit(AFS_VNODE_PSEUDODIR, vnode-flags); - inode-i_flags |= S_NOATIME; + set_bit(AFS_VNODE_MOUNTPOINT, vnode-flags); + inode-i_flags |= S_AUTOMOUNT | S_NOATIME; unlock_new_inode(inode); _leave( = %p, inode); return inode; diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 6d4bc1c..c59bb7c 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -590,6 +590,7 @@ extern const struct inode_operations afs_mntpt_inode_operations; extern const struct inode_operations afs_autocell_inode_operations; extern const struct file_operations afs_mntpt_file_operations; +extern struct vfsmount *afs_d_automount(struct path *); extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *); extern void afs_mntpt_kill_timer(void); diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 6153417..0f7dd7a 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -24,7 +24,6 @@ static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); static int afs_mntpt_open(struct inode *inode, struct file *file); -static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd); static void afs_mntpt_expiry_timed_out(struct work_struct *work); const struct file_operations afs_mntpt_file_operations = { @@ -34,13 +33,11 @@ const struct file_operations afs_mntpt_file_operations = { const struct inode_operations afs_mntpt_inode_operations = { .lookup = afs_mntpt_lookup, - .follow_link= afs_mntpt_follow_link, .readlink = page_readlink, .getattr= afs_getattr, }; const struct inode_operations afs_autocell_inode_operations = { - .follow_link= afs_mntpt_follow_link, .getattr= afs_getattr, }; @@ -88,6 +85,7 @@ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) _debug(symlink is a mountpoint); spin_lock(vnode-lock); set_bit(AFS_VNODE_MOUNTPOINT, vnode-flags); + vnode-vfs_inode.i_flags |= S_AUTOMOUNT; spin_unlock(vnode-lock); } @@ -238,52 +236,37 @@ error_no_devname: } /* - * follow a link from a mountpoint directory, thus causing it to be mounted + * handle an automount point */ -static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) +struct vfsmount *afs_d_automount(struct path *path) { struct vfsmount *newmnt; int err; - _enter(%p{%s},{%s:%p{%s},}, - dentry, - dentry-d_name.name, - nd-path.mnt-mnt_devname, - dentry, - nd-path.dentry-d_name.name); - - dput(nd-path.dentry); - nd-path.dentry = dget(dentry); + _enter({%s,%s}, path-mnt-mnt_devname, path-dentry-d_name.name); - newmnt = afs_mntpt_do_automount(nd-path.dentry); - if (IS_ERR(newmnt)) { - path_put(nd-path); - return (void *)newmnt; - } + newmnt = afs_mntpt_do_automount(path-dentry); + if (IS_ERR(newmnt)) + return newmnt; mntget(newmnt); - err = do_add_mount(newmnt, nd-path, MNT_SHRINKABLE, afs_vfsmounts); + err = do_add_mount(newmnt, path, MNT_SHRINKABLE, afs_vfsmounts); switch (err) { case 0: - path_put(nd-path); - nd-path.mnt = newmnt; - nd-path.dentry = dget(newmnt-mnt_root); schedule_delayed_work(afs_mntpt_expiry_timer, afs_mntpt_expiry_timeout * HZ); - break; + _leave( = %p {%s}, newmnt, newmnt-mnt_devname); + return newmnt; case -EBUSY: /* someone else made a mount here whilst we were busy */ - while (d_mountpoint(nd-path.dentry) - follow_down
[autofs] [PATCH 13/18] autofs4: Clean up dentry operations
From: Ian Kent ra...@themaw.net There are now two distinct dentry operations uses. One for dentrys that trigger mounts and one for dentrys that do not. Rationalize the use of these dentry operations and rename them to reflect their function. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |7 ++- fs/autofs4/inode.c| 12 fs/autofs4/root.c | 36 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index c3b0afe..c28085c 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -206,11 +206,8 @@ extern const struct inode_operations autofs4_symlink_inode_operations; extern const struct inode_operations autofs4_dir_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; - -/* Operations methods */ - -struct vfsmount *autofs4_d_automount(struct path *); -int autofs4_d_manage(struct path *, bool); +extern const struct dentry_operations autofs4_dentry_operations; +extern const struct dentry_operations autofs4_mount_dentry_operations; /* VFS automount flags management functions */ diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index dac3dc7..427c357 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -251,12 +251,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) return ino; } -static const struct dentry_operations autofs4_sb_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - int autofs4_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; @@ -311,7 +305,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_iput; pipe = NULL; - d_set_d_op(root, autofs4_sb_dentry_operations); + d_set_d_op(root, autofs4_dentry_operations); root-d_fsdata = ino; /* Can this call block? */ @@ -322,8 +316,10 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_dput; } - if (autofs_type_trigger(sbi-type)) + if (autofs_type_trigger(sbi-type)) { + d_set_d_op(root, autofs4_mount_dentry_operations); __managed_dentry_set_managed(root); + } root_inode-i_fop = autofs4_root_operations; root_inode-i_op = autofs4_dir_inode_operations; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index ab391d4..b3ab9e9 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -35,6 +35,8 @@ static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); #endif static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); +static struct vfsmount *autofs4_d_automount(struct path *); +static int autofs4_d_manage(struct path *, bool); const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, @@ -64,6 +66,18 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; +/* For dentries that don't initiate mounting */ +const struct dentry_operations autofs4_dentry_operations = { + .d_release = autofs4_dentry_release, +}; + +/* For dentries that do initiate mounting */ +const struct dentry_operations autofs4_mount_dentry_operations = { + .d_automount= autofs4_d_automount, + .d_manage = autofs4_d_manage, + .d_release = autofs4_dentry_release, +}; + static void autofs4_add_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -158,18 +172,6 @@ void autofs4_dentry_release(struct dentry *de) } } -/* For dentries of directories in the root dir */ -static const struct dentry_operations autofs4_root_dentry_operations = { - .d_release = autofs4_dentry_release, -}; - -/* For other dentries */ -static const struct dentry_operations autofs4_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - static struct dentry *autofs4_lookup_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -337,7 +339,7 @@ static struct dentry *autofs4_mountpoint_changed(struct path *path) return path-dentry; } -struct vfsmount *autofs4_d_automount(struct path *path) +static struct vfsmount *autofs4_d_automount(struct path *path) { struct dentry *dentry = path-dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -506,7 +508,7 @@ static struct dentry *autofs4_lookup
[autofs] [PATCH 07/18] Add more dentry flags for special function directories
Add more flags to the d_flags in struct dentry for special function directories such as mountpoints and autofs substructures. The relevant flags are: (*) DCACHE_MOUNTED. (Already exists). Indicates that this dentry has things mounted upon it. (*) DCACHE_NEED_AUTOMOUNT. This is a reflection of the S_AUTOMOUNT inode flag. This is reflected by d_set_d_op(). follow_automount() is now keyed off of this rather than being keyed off S_AUTOMOUNT directly. Possibly S_AUTOMOUNT should shift to the dentry entirely. This may also be tweaked live (such as by the autofs4 filesystem) to alter the effect. (*) DCACHE_MANAGE_TRANSIT. This is an indicator that the filesystem that owns the dentry wants to manage processes transiting away from that dentry. If this is set on a dentry, then a new dentry op: int (*d_manage)(struct path *); is invoked. This is allowed to sleep and is allowed to return an error. This allows autofs to hold non-Oz-mode processes here without any filesystem locks being held. __follow_mount() is replaced by managed_dentry() which now handles transit to a mountpoint's root dentry, automount points and points that the filesystem wants to manage. == WHAT THIS MEANS FOR AUTOFS == autofs currently uses the lookup() inode op and the d_revalidate() dentry op to trigger the automounting of indirect mounts, and both of these can be called with i_mutex held. autofs knows that the i_mutex will be held by the caller in lookup(), and so can drop it before invoking the daemon - but this isn't so for d_revalidate(), since the lock is only held on _some_ of the code paths that call it. This means that autofs can't risk dropping i_mutex from its d_revalidate() function before it calls the daemon. The bug could manifest itself as, for example, a process that's trying to validate an automount dentry that gets made to wait because that dentry is expired and needs cleaning up: mkdir S 8014e05a 0 32580 24956 Call Trace: [885371fd] :autofs4:autofs4_wait+0x674/0x897 [80127f7d] avc_has_perm+0x46/0x58 [8009fdcf] autoremove_wake_function+0x0/0x2e [88537be6] :autofs4:autofs4_expire_wait+0x41/0x6b [88535cfc] :autofs4:autofs4_revalidate+0x91/0x149 [80036d96] __lookup_hash+0xa0/0x12f [80057a2f] lookup_create+0x46/0x80 [800e6e31] sys_mkdirat+0x56/0xe4 versus the automount daemon which wants to remove that dentry, but can't because the normal process is holding the i_mutex lock: automount D 8014e05a 0 32581 1 32561 Call Trace: [80063c3f] __mutex_lock_slowpath+0x60/0x9b [8000ccf1] do_path_lookup+0x2ca/0x2f1 [80063c89] .text.lock.mutex+0xf/0x14 [800e6d55] do_rmdir+0x77/0xde [8005d229] tracesys+0x71/0xe0 [8005d28d] tracesys+0xd5/0xe0 which means that the system is deadlocked. This patch allows autofs to hold up normal processes whilst the daemon goes ahead and does things to the dentry tree behind the automouter point without risking a deadlock as no locks are held in d_manage() or d_automount(). Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- Documentation/filesystems/vfs.txt | 13 +++ fs/dcache.c |2 fs/namei.c| 153 ++--- include/linux/dcache.h| 13 ++- 4 files changed, 130 insertions(+), 51 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index bb8d277..99f0127 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -865,6 +865,7 @@ struct dentry_operations { void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); + int (*d_manage)(struct path *); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -940,8 +941,16 @@ struct dentry_operations { the automount first. If the automount failed, then an error code should be returned. - This function is only used if S_AUTOMOUNT is set on the inode to which - the dentry refers. + This function is only used if DMANAGED_AUTOMOUNT is set on the dentry. + This is set by d_add() if S_AUTOMOUNT is set on the inode being added. + + d_manage: called to allow the filesystem to manage the transition from a + dentry (optional). This allows autofs, for example, to hold up clients + waiting to explore behind a 'mountpoint', whilst letting the daemon go + past and construct the subtree
[autofs] [PATCH 05/18] Remove the automount through follow_link() kludge code from pathwalk
Remove the automount through follow_link() kludge code from pathwalk in favour of using d_automount(). Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/namei.c | 17 +++-- 1 files changed, 3 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 159da29..dfafb97 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1193,17 +1193,6 @@ fail: } /* - * This is a temporary kludge to deal with automount symlinks; proper - * solution is to trigger them on follow_mount(), so that do_lookup() - * would DTRT. To be killed before 2.6.34-final. - */ -static inline int follow_on_final(struct inode *inode, unsigned lookup_flags) -{ - return inode unlikely(inode-i_op-follow_link) - ((lookup_flags LOOKUP_FOLLOW) || S_ISDIR(inode-i_mode)); -} - -/* * Name resolution. * This is the basic name resolution function, turning a pathname into * the final dentry. We expect 'base' to be positive and a directory. @@ -1341,7 +1330,8 @@ last_component: err = do_lookup(nd, this, next, inode); if (err) break; - if (follow_on_final(inode, lookup_flags)) { + if (inode unlikely(inode-i_op-follow_link) + (lookup_flags LOOKUP_FOLLOW)) { if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry)) return -ECHILD; BUG_ON(inode != next.dentry-d_inode); @@ -2390,8 +2380,7 @@ reval: struct path holder; void *cookie; error = -ELOOP; - /* S_ISDIR part is a temporary automount kludge */ - if (!(nd.flags LOOKUP_FOLLOW) !S_ISDIR(nd.inode-i_mode)) + if (!(nd.flags LOOKUP_FOLLOW)) goto exit_dput; if (count++ == 32) goto exit_dput; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 04/18] CIFS: Use d_automount() rather than abusing follow_link()
Make CIFS use the new d_automount() dentry operation rather than abusing follow_link() on directories. [NOTE: THIS IS UNTESTED!] Signed-off-by: David Howells dhowe...@redhat.com Cc: Steve French sfre...@samba.org --- fs/cifs/cifs_dfs_ref.c | 134 fs/cifs/cifsfs.h |6 ++ fs/cifs/dir.c |2 + fs/cifs/inode.c|8 ++- 4 files changed, 80 insertions(+), 70 deletions(-) diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index c68a056..ddd0b3e 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -255,35 +255,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, } -static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, - struct list_head *mntlist) -{ - /* stolen from afs code */ - int err; - - mntget(newmnt); - err = do_add_mount(newmnt, nd-path, nd-path.mnt-mnt_flags | MNT_SHRINKABLE, mntlist); - switch (err) { - case 0: - path_put(nd-path); - nd-path.mnt = newmnt; - nd-path.dentry = dget(newmnt-mnt_root); - schedule_delayed_work(cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); - break; - case -EBUSY: - /* someone else made a mount here whilst we were busy */ - while (d_mountpoint(nd-path.dentry) - follow_down(nd-path)) - ; - err = 0; - default: - mntput(newmnt); - break; - } - return err; -} - static void dump_referral(const struct dfs_info3_param *ref) { cFYI(1, DFS: ref path: %s, ref-path_name); @@ -293,45 +264,43 @@ static void dump_referral(const struct dfs_info3_param *ref) ref-path_consumed); } - -static void* -cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +/* + * Create a vfsmount that we can automount + */ +static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) { struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; - char *full_path = NULL; + char *full_path; int xid, i; - int rc = 0; - struct vfsmount *mnt = ERR_PTR(-ENOENT); + int rc; + struct vfsmount *mnt; struct tcon_link *tlink; cFYI(1, in %s, __func__); - BUG_ON(IS_ROOT(dentry)); + BUG_ON(IS_ROOT(mntpt)); xid = GetXid(); - dput(nd-path.dentry); - nd-path.dentry = dget(dentry); - /* * The MSDFS spec states that paths in DFS referral requests and * responses must be prefixed by a single '\' character instead of * the double backslashes usually used in the UNC. This function * gives us the latter, so we must adjust the result. */ - full_path = build_path_from_dentry(dentry); - if (full_path == NULL) { - rc = -ENOMEM; - goto out_err; - } + mnt = ERR_PTR(-ENOMEM); + full_path = build_path_from_dentry(mntpt); + if (full_path == NULL) + goto free_xid; - cifs_sb = CIFS_SB(dentry-d_inode-i_sb); + cifs_sb = CIFS_SB(mntpt-d_inode-i_sb); tlink = cifs_sb_tlink(cifs_sb); + mnt = ERR_PTR(-EINVAL); if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto out_err; + mnt = ERR_CAST(tlink); + goto free_full_path; } ses = tlink_tcon(tlink)-ses; @@ -341,46 +310,77 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) cifs_put_tlink(tlink); + mnt = ERR_PTR(-ENOENT); for (i = 0; i num_referrals; i++) { int len; - dump_referral(referrals+i); + dump_referral(referrals + i); /* connect to a node */ len = strlen(referrals[i].node_name); if (len 2) { cERROR(1, %s: Net Address path too short: %s, __func__, referrals[i].node_name); - rc = -EINVAL; - goto out_err; + mnt = ERR_PTR(-EINVAL); + break; } mnt = cifs_dfs_do_refmount(cifs_sb, full_path, referrals + i); cFYI(1, %s: cifs_dfs_do_refmount:%s , mnt:%p, __func__, referrals[i].node_name, mnt); - - /* complete mount procedure if we accured submount */ if (!IS_ERR(mnt)) - break; + goto success; } - /* we need it cause for() above
Re: [autofs] [PATCH 00/17] Introduce automounter dentry ops
Stef Bon stef...@gmail.com wrote: As I understand it, it gives a framework to allow the fs (cifs, nfs afs) do sub-auto-mounting right? I suppose. Is there a difference between automounting and sub-automounting? :-) David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 08/17] Make follow_down() handle d_manage()
The previous patch (that adds d_manage()) offers autofs the opportunity to block processes whilst it is rearranging its dentry tree, but only covers cases where managed_dentry() is called. Some places call follow_down(), which would allow processes to bypass autofs's attempts to block them. Make follow_down() handle managed dentries. Do this by renaming follow_down() to follow_down_one() and instituting a new follow_down(). follow_down_one() is then only used where a call to d_manage() is not needed. follow_down() then incorporates the loop from its remaining callers to follow down through all mounted filesystems at that point. Before each mountpoint is transited and if requested by the filesystem, d_manage() is called to hold or reject that transit. The callers of follow_down() must then handle a possible error condition. follow_down() is given a parameter to say whether someone is trying to mount on that point (and holding namespace_sem). This is now passed on to d_manage(). The filesystem may reject this request by returning an error from d_manage(). Furthermore, d_manage() may end follow_down() processing early by returning -EISDIR to indicate it wants the dentry to be dealt with as an ordinary directory, not a mountpoint. This permits autofs to let its daemon see the underlying dentry. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/autofs/dirhash.c|5 ++-- fs/autofs4/autofs_i.h | 13 --- fs/autofs4/dev-ioctl.c |2 +- fs/autofs4/expire.c|2 +- fs/autofs4/root.c | 11 - fs/namei.c | 57 +--- fs/namespace.c | 14 +++- fs/nfsd/vfs.c |5 +++- include/linux/dcache.h |7 +- include/linux/namei.h |3 ++- 10 files changed, 82 insertions(+), 37 deletions(-) diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index e947915..a24092c 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -85,13 +85,12 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, } path.mnt = mnt; path_get(path); - if (!follow_down(path)) { + if (!follow_down_one(path)) { path_put(path); DPRINTK((autofs: not expirable (not a mounted directory): %s\n, ent-name)); continue; } - while (d_mountpoint(path.dentry) follow_down(path)) - ; + follow_down(path, false); // TODO: need to check error umount_ok = may_umount(path.mnt); path_put(path); diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 3d283ab..08af160 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -226,19 +226,6 @@ 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 path *path) -{ - int res = 0; - - while (d_mountpoint(path-dentry)) { - int followed = follow_down(path); - 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); diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index ba4a38b..8567abc 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -551,7 +551,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, err = have_submounts(path.dentry); - if (follow_down(path)) + if (follow_down_one(path)) magic = path.mnt-mnt_sb-s_magic; } diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 9f5bde2..47feba9 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -56,7 +56,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) path_get(path); - if (!follow_down(path)) + if (!follow_down_one(path)) goto done; if (is_autofs4_dentry(path.dentry)) { diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index cb1bd38..7dd218b 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -227,7 +227,7 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) nd-flags); /* * For an expire of a covered direct or offset mount we need -* to break out of follow_down() at the autofs mount trigger +* to break out of follow_down_one() 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. */ @@ -236,7 +236,7
[autofs] [PATCH 12/17] autofs4: cleanup inode operations
From: Ian Kent ra...@themaw.net Since the use of -follow_link() has been eliminated there is no need to separate the indirect and direct inode operations. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |3 --- fs/autofs4/inode.c|4 +--- fs/autofs4/root.c | 15 --- 3 files changed, 1 insertions(+), 21 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index cb86036..8710878 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -201,9 +201,6 @@ void autofs_dev_ioctl_exit(void); extern const struct inode_operations autofs4_symlink_inode_operations; extern const struct inode_operations autofs4_dir_inode_operations; -extern const struct inode_operations autofs4_root_inode_operations; -extern const struct inode_operations autofs4_indirect_root_inode_operations; -extern const struct inode_operations autofs4_direct_root_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index c6e75b3..80cdffd 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -326,9 +326,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) __managed_dentry_set_managed(root); root_inode-i_fop = autofs4_root_operations; - root_inode-i_op = autofs_type_trigger(sbi-type) ? - autofs4_direct_root_inode_operations : - autofs4_indirect_root_inode_operations; + root_inode-i_op = autofs4_dir_inode_operations; /* Couldn't this be tested earlier? */ if (sbi-max_proto AUTOFS_MIN_PROTO_VERSION || diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 77257dd..2856a0c 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -52,21 +52,6 @@ const struct file_operations autofs4_dir_operations = { .llseek = dcache_dir_lseek, }; -const 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, -}; - -const struct inode_operations autofs4_direct_root_inode_operations = { - .lookup = autofs4_lookup, - .unlink = autofs4_dir_unlink, - .mkdir = autofs4_dir_mkdir, - .rmdir = autofs4_dir_rmdir, -}; - const struct inode_operations autofs4_dir_inode_operations = { .lookup = autofs4_lookup, .unlink = autofs4_dir_unlink, ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 07/17] Make dentry::d_mounted into a more general field for special function dirs
Make the d_mounted field in struct dentry into a more general field for special function directories such as mountpoints and autofs substructures. d_mounted is renamed d_managed, and that is split into three fields: (*) #define DMANAGED_MOUNTPOINT0x0fff This is the mounted-on-this-dentry count as per d_mounted. (*) #define DMANAGED_AUTOMOUNT 0x1000 This is a reflection of the S_AUTOMOUNT inode flag. This is reflected by __d_instantiate(). follow_automount() is now keyed off of this rather than being keyed off S_AUTOMOUNT directly. Possibly S_AUTOMOUNT should shift to the dentry entirely. (*) #define DMANAGED_TRANSIT 0x2000 This is an indicator that the filesystem that owns the dentry wants to manage processes transiting away from that dentry. If this is set on a dentry, then a new dentry op: int (*d_manage)(struct path *); is invoked. This is allowed to sleep and is allowed to return an error. This allows autofs to hold non-Oz-mode processes here without any filesystem locks being held. Since d_managed is just an integer, all three fields can be tested in one go, reducing the amount of intrusion into the normal path. __follow_mount() is replaced by managed_dentry() which now handles transit to a mountpoint's root dentry, automount points and points that the filesystem wants to manage. == WHAT THIS MEANS FOR AUTOFS == autofs currently uses the lookup() inode op and the d_revalidate() dentry op to trigger the automounting of indirect mounts, and both of these can be called with i_mutex held. autofs knows that the i_mutex will be held by the caller in lookup(), and so can drop it before invoking the daemon - but this isn't so for d_revalidate(), since the lock is only held on _some_ of the code paths that call it. This means that autofs can't risk dropping i_mutex from its d_revalidate() function before it calls the daemon. The bug could manifest itself as, for example, a process that's trying to validate an automount dentry that gets made to wait because that dentry is expired and needs cleaning up: mkdir S 8014e05a 0 32580 24956 Call Trace: [885371fd] :autofs4:autofs4_wait+0x674/0x897 [80127f7d] avc_has_perm+0x46/0x58 [8009fdcf] autoremove_wake_function+0x0/0x2e [88537be6] :autofs4:autofs4_expire_wait+0x41/0x6b [88535cfc] :autofs4:autofs4_revalidate+0x91/0x149 [80036d96] __lookup_hash+0xa0/0x12f [80057a2f] lookup_create+0x46/0x80 [800e6e31] sys_mkdirat+0x56/0xe4 versus the automount daemon which wants to remove that dentry, but can't because the normal process is holding the i_mutex lock: automount D 8014e05a 0 32581 1 32561 Call Trace: [80063c3f] __mutex_lock_slowpath+0x60/0x9b [8000ccf1] do_path_lookup+0x2ca/0x2f1 [80063c89] .text.lock.mutex+0xf/0x14 [800e6d55] do_rmdir+0x77/0xde [8005d229] tracesys+0x71/0xe0 [8005d28d] tracesys+0xd5/0xe0 which means that the system is deadlocked. This patch allows autofs to hold up normal processes whilst the daemon goes ahead and does things to the dentry tree behind the automouter point without risking a deadlock as no locks are held in d_manage() or d_automount(). Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- Documentation/filesystems/vfs.txt | 13 +++- fs/autofs4/expire.c |4 + fs/dcache.c |7 ++ fs/namei.c| 118 + fs/namespace.c|6 +- include/linux/dcache.h| 15 +++-- 6 files changed, 110 insertions(+), 53 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index ff4bf82..fc27e43 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -848,6 +848,7 @@ struct dentry_operations { void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); + int (*d_manage)(struct path *); }; d_revalidate: called when the VFS needs to revalidate a dentry. This @@ -891,8 +892,16 @@ struct dentry_operations { the automount first. If the automount failed, then an error code should be returned. - This function is only used if S_AUTOMOUNT is set on the inode to which - the dentry refers. + This function is only used if DMANAGED_AUTOMOUNT is set on the dentry. + This is set by d_add() if S_AUTOMOUNT is set on the inode being added. + + d_manage: called to allow the filesystem
[autofs] [PATCH 14/17] autofs4: cleanup autofs4_free_ino()
From: Ian Kent ra...@themaw.net When this function is called the local reference count does't need to be updated since the dentry is going away and dput definitely must not be called here. Also the autofs info struct field inode isn't used so remove it. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/inode.c | 13 - fs/autofs4/root.c |9 - 2 files changed, 0 insertions(+), 22 deletions(-) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 8eaa723..ebc6354 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -45,7 +45,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, if (!reinit) { ino-flags = 0; - ino-inode = NULL; ino-dentry = NULL; ino-size = 0; INIT_LIST_HEAD(ino-active); @@ -76,19 +75,8 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, 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) { - 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) @@ -390,7 +378,6 @@ struct inode *autofs4_get_inode(struct super_block *sb, if (inode == NULL) return NULL; - inf-inode = inode; inode-i_mode = inf-mode; if (sb-s_root) { inode-i_uid = sb-s_root-d_inode-i_uid; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index cb168ba..3b38794 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -144,11 +144,8 @@ void autofs4_dentry_release(struct dentry *de) DPRINTK(releasing %p, de); inf = autofs4_dentry_ino(de); - 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)) @@ -157,10 +154,6 @@ void autofs4_dentry_release(struct dentry *de) list_del(inf-expiring); spin_unlock(sbi-lookup_lock); } - - inf-dentry = NULL; - inf-inode = NULL; - autofs4_free_ino(inf); } } @@ -582,7 +575,6 @@ static int autofs4_dir_symlink(struct inode *dir, 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; @@ -708,7 +700,6 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) p_ino = autofs4_dentry_ino(dentry-d_parent); if (p_ino dentry-d_parent != dentry) atomic_inc(p_ino-count); - ino-inode = inode; inc_nlink(dir); dir-i_mtime = CURRENT_TIME; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 15/17] autofs4 - fix wait validation
From: Ian Kent ra...@themaw.net It is possible for the check in wait.c:validate_request() to return an incorrect result if the dentry that was mounted upon has changed during the callback. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/waitq.c | 17 - 1 files changed, 16 insertions(+), 1 deletions(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 2341375..f3792ff 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -296,6 +296,9 @@ static int validate_request(struct autofs_wait_queue **wait, * completed while we waited on the mutex ... */ if (notify == NFY_MOUNT) { + struct dentry *new = NULL; + int valid = 1; + /* * If the dentry was successfully mounted while we slept * on the wait queue mutex we can return success. If it @@ -303,8 +306,20 @@ static int validate_request(struct autofs_wait_queue **wait, * a multi-mount with no mount at it's base) we can * continue on and create a new request. */ + if (!IS_ROOT(dentry)) { + if (dentry-d_inode d_unhashed(dentry)) { + struct dentry *parent = dentry-d_parent; + new = d_lookup(parent, dentry-d_name); + if (new) + dentry = new; + } + } if (have_submounts(dentry)) - return 0; + valid = 0; + + if (new) + dput(new); + return valid; } return 1; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 06/17] Add an AT_NO_AUTOMOUNT flag to suppress terminal automount
Add an AT_NO_AUTOMOUNT flag to suppress terminal automounting of directories with follow_link semantics. This can be used by fstatat() users to permit the gathering of attributes on an automount point and also prevent mass-automounting of a directory of automount points by ls. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/namei.c|6 ++ fs/stat.c |4 +++- include/linux/fcntl.h |1 + include/linux/namei.h |2 ++ 4 files changed, 12 insertions(+), 1 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 86421f9..74bce3a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -625,6 +625,12 @@ static int follow_automount(struct path *path, unsigned flags, if (!path-dentry-d_op || !path-dentry-d_op-d_automount) return -EREMOTE; + /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT +* and this is the terminal part of the path. +*/ + if ((flags LOOKUP_NO_AUTOMOUNT) !(flags LOOKUP_CONTINUE)) + return -EXDEV; /* we actually want to stop here */ + /* We want to mount if someone is trying to open/create a file of any * type under the mountpoint, wants to traverse through the mountpoint * or wants to open the mounted directory. diff --git a/fs/stat.c b/fs/stat.c index 12e90e2..d5c61cf 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -75,11 +75,13 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int error = -EINVAL; int lookup_flags = 0; - if ((flag ~AT_SYMLINK_NOFOLLOW) != 0) + if ((flag ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0) goto out; if (!(flag AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; + if (flag AT_NO_AUTOMOUNT) + lookup_flags |= LOOKUP_NO_AUTOMOUNT; error = user_path_at(dfd, filename, lookup_flags, path); if (error) diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index afc00af..a562fa5 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -45,6 +45,7 @@ #define AT_REMOVEDIR 0x200 /* Remove directory instead of unlinking file. */ #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ +#define AT_NO_AUTOMOUNT0x800 /* Suppress terminal automount traversal */ #ifdef __KERNEL__ diff --git a/include/linux/namei.h b/include/linux/namei.h index 05b441d..1e1febf 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -43,12 +43,14 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; * - internal there are more path components flag * - locked when lookup done with dcache_lock held * - dentry cache is untrusted; force a real lookup + * - suppress terminal automount */ #define LOOKUP_FOLLOW 1 #define LOOKUP_DIRECTORY2 #define LOOKUP_CONTINUE 4 #define LOOKUP_PARENT 16 #define LOOKUP_REVAL 64 +#define LOOKUP_NO_AUTOMOUNT128 /* * Intent data */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 05/17] Remove the automount through follow_link() kludge code from pathwalk
Remove the automount through follow_link() kludge code from pathwalk in favour of using d_automount(). Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Ian Kent ra...@themaw.net --- fs/namei.c | 17 +++-- 1 files changed, 3 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c50b9d7..86421f9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -861,17 +861,6 @@ fail: } /* - * This is a temporary kludge to deal with automount symlinks; proper - * solution is to trigger them on follow_mount(), so that do_lookup() - * would DTRT. To be killed before 2.6.34-final. - */ -static inline int follow_on_final(struct inode *inode, unsigned lookup_flags) -{ - return inode unlikely(inode-i_op-follow_link) - ((lookup_flags LOOKUP_FOLLOW) || S_ISDIR(inode-i_mode)); -} - -/* * Name resolution. * This is the basic name resolution function, turning a pathname into * the final dentry. We expect 'base' to be positive and a directory. @@ -991,7 +980,8 @@ last_component: if (err) break; inode = next.dentry-d_inode; - if (follow_on_final(inode, lookup_flags)) { + if (inode unlikely(inode-i_op-follow_link) + (lookup_flags LOOKUP_FOLLOW)) { err = do_follow_link(next, nd); if (err) goto return_err; @@ -1882,8 +1872,7 @@ reval: struct inode *inode = path.dentry-d_inode; void *cookie; error = -ELOOP; - /* S_ISDIR part is a temporary automount kludge */ - if (!(nd.flags LOOKUP_FOLLOW) !S_ISDIR(inode-i_mode)) + if (!(nd.flags LOOKUP_FOLLOW)) goto exit_dput; if (count++ == 32) goto exit_dput; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 04/17] CIFS: Use d_automount() rather than abusing follow_link()
Make CIFS use the new d_automount() dentry operation rather than abusing follow_link() on directories. [NOTE: THIS IS UNTESTED!] [Question: Why does cifs_dfs_do_refmount() when the caller has already done that and could pass the result through?] Signed-off-by: David Howells dhowe...@redhat.com Cc: Steve French sfre...@samba.org --- fs/cifs/cifs_dfs_ref.c | 134 +++- fs/cifs/cifsfs.h |6 ++ fs/cifs/dir.c |2 + fs/cifs/inode.c|8 ++- 4 files changed, 78 insertions(+), 72 deletions(-) diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d6ced7a..8fc4943 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -256,35 +256,6 @@ static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, } -static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, - struct list_head *mntlist) -{ - /* stolen from afs code */ - int err; - - mntget(newmnt); - err = do_add_mount(newmnt, nd-path, nd-path.mnt-mnt_flags | MNT_SHRINKABLE, mntlist); - switch (err) { - case 0: - path_put(nd-path); - nd-path.mnt = newmnt; - nd-path.dentry = dget(newmnt-mnt_root); - schedule_delayed_work(cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); - break; - case -EBUSY: - /* someone else made a mount here whilst we were busy */ - while (d_mountpoint(nd-path.dentry) - follow_down(nd-path)) - ; - err = 0; - default: - mntput(newmnt); - break; - } - return err; -} - static void dump_referral(const struct dfs_info3_param *ref) { cFYI(1, DFS: ref path: %s, ref-path_name); @@ -294,34 +265,30 @@ static void dump_referral(const struct dfs_info3_param *ref) ref-path_consumed); } - -static void* -cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +/* + * Create a vfsmount that we can automount + */ +static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) { struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; - char *full_path = NULL; + char *full_path; int xid, i; - int rc = 0; - struct vfsmount *mnt = ERR_PTR(-ENOENT); + int rc; + struct vfsmount *mnt; cFYI(1, in %s, __func__); - BUG_ON(IS_ROOT(dentry)); + BUG_ON(IS_ROOT(mntpt)); xid = GetXid(); - dput(nd-path.dentry); - nd-path.dentry = dget(dentry); - - cifs_sb = CIFS_SB(dentry-d_inode-i_sb); + cifs_sb = CIFS_SB(mntpt-d_inode-i_sb); + mnt = ERR_PTR(-EINVAL); ses = cifs_sb-tcon-ses; - - if (!ses) { - rc = -EINVAL; - goto out_err; - } + if (!ses) + goto free_xid; /* * The MSDFS spec states that paths in DFS referral requests and @@ -329,56 +296,85 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) * the double backslashes usually used in the UNC. This function * gives us the latter, so we must adjust the result. */ - full_path = build_path_from_dentry(dentry); - if (full_path == NULL) { - rc = -ENOMEM; - goto out_err; - } + mnt = ERR_PTR(-ENOMEM); + full_path = build_path_from_dentry(mntpt); + if (full_path == NULL) + goto free_xid; rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb-local_nls, num_referrals, referrals, cifs_sb-mnt_cifs_flags CIFS_MOUNT_MAP_SPECIAL_CHR); + mnt = ERR_PTR(-ENOENT); for (i = 0; i num_referrals; i++) { int len; - dump_referral(referrals+i); + dump_referral(referrals + i); /* connect to a node */ len = strlen(referrals[i].node_name); if (len 2) { cERROR(1, %s: Net Address path too short: %s, __func__, referrals[i].node_name); - rc = -EINVAL; - goto out_err; + mnt = ERR_PTR(-EINVAL); + break; } mnt = cifs_dfs_do_refmount(cifs_sb, full_path, referrals + i); cFYI(1, %s: cifs_dfs_do_refmount:%s , mnt:%p, __func__, referrals[i].node_name, mnt); - - /* complete mount procedure if we accured submount */ if (!IS_ERR(mnt)) - break
[autofs] [PATCH 03/17] NFS: Use d_automount() rather than abusing follow_link()
Make NFS use the new d_automount() dentry operation rather than abusing follow_link() on directories. Signed-off-by: David Howells dhowe...@redhat.com Acked-by: Trond Myklebust trond.mykleb...@netapp.com Acked-by: Ian Kent ra...@themaw.net --- fs/nfs/dir.c |4 ++ fs/nfs/inode.c |4 +- fs/nfs/internal.h |1 + fs/nfs/namespace.c | 87 ++-- include/linux/nfs_fs.h |1 - 5 files changed, 46 insertions(+), 51 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e257172..46f2e12 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -734,7 +734,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) { struct nfs_server *server = NFS_SERVER(inode); - if (test_bit(NFS_INO_MOUNTPOINT, NFS_I(inode)-flags)) + if (IS_AUTOMOUNT(inode)) return 0; if (nd != NULL) { /* VFS wants an on-the-wire revalidation */ @@ -934,6 +934,7 @@ const struct dentry_operations nfs_dentry_operations = { .d_revalidate = nfs_lookup_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput, + .d_automount= nfs_d_automount, }; static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) @@ -1009,6 +1010,7 @@ const struct dentry_operations nfs4_dentry_operations = { .d_revalidate = nfs_open_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput, + .d_automount= nfs_d_automount, }; /* diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 7d2d6c7..d554949 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -302,7 +302,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) else inode-i_op = nfs_mountpoint_inode_operations; inode-i_fop = NULL; - set_bit(NFS_INO_MOUNTPOINT, nfsi-flags); + inode-i_flags |= S_AUTOMOUNT; } } else if (S_ISLNK(inode-i_mode)) inode-i_op = nfs_symlink_inode_operations; @@ -1205,7 +1205,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) /* Update the fsid? */ if (S_ISDIR(inode-i_mode) (fattr-valid NFS_ATTR_FATTR_FSID) !nfs_fsid_equal(server-fsid, fattr-fsid) - !test_bit(NFS_INO_MOUNTPOINT, nfsi-flags)) + !IS_AUTOMOUNT(inode)) server-fsid = fattr-fsid; /* diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c961bc9..45b98e7 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -239,6 +239,7 @@ extern char *nfs_path(const char *base, const struct dentry *droot, const struct dentry *dentry, char *buffer, ssize_t buflen); +extern struct vfsmount *nfs_d_automount(struct path *path); /* getroot.c */ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index db6aa36..bf80079 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -88,9 +88,8 @@ Elong: } /* - * nfs_follow_mountpoint - handle crossing a mountpoint on the server - * @dentry - dentry of mountpoint - * @nd - nameidata info + * nfs_d_automount - Handle crossing a mountpoint on the server + * @path - The mountpoint * * When we encounter a mountpoint on the server, we want to set up * a mountpoint on the client too, to prevent inode numbers from @@ -100,87 +99,81 @@ Elong: * situation, and that different filesystems may want to use * different security flavours. */ -static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +struct vfsmount *nfs_d_automount(struct path *path) { struct vfsmount *mnt; - struct nfs_server *server = NFS_SERVER(dentry-d_inode); + struct nfs_server *server = NFS_SERVER(path-dentry-d_inode); struct dentry *parent; struct nfs_fh *fh = NULL; struct nfs_fattr *fattr = NULL; int err; - dprintk(-- nfs_follow_mountpoint()\n); + dprintk(-- nfs_d_automount()\n); - err = -ESTALE; - if (IS_ROOT(dentry)) - goto out_err; + mnt = ERR_PTR(-ESTALE); + if (IS_ROOT(path-dentry)) + goto out_nofree; - err = -ENOMEM; + mnt = ERR_PTR(-ENOMEM); fh = nfs_alloc_fhandle(); fattr = nfs_alloc_fattr(); if (fh == NULL || fattr == NULL) - goto out_err; + goto out; dprintk(%s: enter\n, __func__); - dput(nd-path.dentry); - nd-path.dentry = dget(dentry); - /* Look it up again */ - parent = dget_parent(nd-path.dentry); + /* Look
[autofs] [PATCH 10/17] autofs4: add d_manage() dentry operation
From: Ian Kent ra...@themaw.net This patch required a previous patch to add the -d_automount() dentry operation. Add a function to use the newly defined -d_manage() dentry operation for blocking during mount and expire. Whether the VFS calls the dentry operations d_automount() and d_manage() is controled by the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags. autofs uses the d_automount() operation to callback to user space to request mount operations and the d_manage() operation to block walks into mounts that are under construction or destruction. In order to prevent these functions from being called unnecessarily the DMANAGED_* flags are cleared for cases which would cause this. In the common case the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags are both set for dentrys waiting to be mounted. The DMANAGED_TRANSIT flag is cleared upon successful mount request completion and set during expire runs, both during the dentry expire check, and if selected for expire, is left set until a subsequent successful mount request completes. The exception to this is the so-called rootless multi-mount which has no actual mount at its base. In this case the DMANAGED_AUTOMOUNT flag is cleared upon successful mount request completion as well and set again after a successful expire. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h | 50 - fs/autofs4/expire.c | 42 + fs/autofs4/inode.c|3 + fs/autofs4/root.c | 100 +++-- 4 files changed, 164 insertions(+), 31 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 079206c..48f8248 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -96,7 +96,6 @@ struct autofs_info { }; #define AUTOFS_INF_EXPIRING(10) /* dentry is in the process of expiring */ -#define AUTOFS_INF_MOUNTPOINT (11) /* mountpoint status for direct expire */ #define AUTOFS_INF_PENDING (12) /* dentry pending mount */ struct autofs_wait_queue { @@ -218,6 +217,7 @@ extern const struct file_operations autofs4_root_operations; /* Operations methods */ struct vfsmount *autofs4_d_automount(struct path *); +int autofs4_d_manage(struct path *, bool); /* VFS automount flags management functions */ @@ -245,6 +245,54 @@ static inline void managed_dentry_clear_automount(struct dentry *dentry) spin_unlock(dentry-d_lock); } +static inline void __managed_dentry_set_transit(struct dentry *dentry) +{ + dentry-d_managed |= DMANAGED_TRANSIT; +} + +static inline void managed_dentry_set_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_transit(struct dentry *dentry) +{ + dentry-d_managed = ~DMANAGED_TRANSIT; +} + +static inline void managed_dentry_clear_transit(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_transit(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_set_managed(struct dentry *dentry) +{ + dentry-d_managed |= (DMANAGED_AUTOMOUNT|DMANAGED_TRANSIT); +} + +static inline void managed_dentry_set_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_set_managed(dentry); + spin_unlock(dentry-d_lock); +} + +static inline void __managed_dentry_clear_managed(struct dentry *dentry) +{ + dentry-d_managed = ~(DMANAGED_AUTOMOUNT|DMANAGED_TRANSIT); +} + +static inline void managed_dentry_clear_managed(struct dentry *dentry) +{ + spin_lock(dentry-d_lock); + __managed_dentry_clear_managed(dentry); + spin_unlock(dentry-d_lock); +} + /* Initializing function */ int autofs4_fill_super(struct super_block *, void *, int); diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index c366dac..5901da5 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -26,10 +26,6 @@ static inline int autofs4_can_expire(struct dentry *dentry, if (ino == NULL) return 0; - /* No point expiring a pending mount */ - if (ino-flags AUTOFS_INF_PENDING) - return 0; - if (!do_now) { /* Too young to die */ if (!timeout || time_after(ino-last_used + timeout, now)) @@ -264,6 +260,7 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, unsigned long timeout; struct dentry *root = dget(sb-s_root); int do_now = how AUTOFS_EXP_IMMEDIATE; + struct autofs_info *ino; if (!root) return NULL; @@ -272,18 +269,21 @@ struct dentry *autofs4_expire_direct(struct super_block *sb, timeout = sbi-exp_timeout; spin_lock(sbi-fs_lock); + ino = autofs4_dentry_ino(root); + /* No point expiring a pending mount */ + if (ino
[autofs] [PATCH 13/17] autofs4: cleanup dentry operations
From: Ian Kent ra...@themaw.net There are now two distinct dentry operations uses. One for dentrys that trigger mounts and one for dentrys that do not. Rationalize the use of these dentry operations and rename them to reflect their function. Signed-off-by: Ian Kent ra...@themaw.net Signed-off-by: David Howells dhowe...@redhat.com --- fs/autofs4/autofs_i.h |7 ++- fs/autofs4/inode.c| 12 fs/autofs4/root.c | 36 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 8710878..8b1c0bc 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -203,11 +203,8 @@ extern const struct inode_operations autofs4_symlink_inode_operations; extern const struct inode_operations autofs4_dir_inode_operations; extern const struct file_operations autofs4_dir_operations; extern const struct file_operations autofs4_root_operations; - -/* Operations methods */ - -struct vfsmount *autofs4_d_automount(struct path *); -int autofs4_d_manage(struct path *, bool); +extern const struct dentry_operations autofs4_dentry_operations; +extern const struct dentry_operations autofs4_mount_dentry_operations; /* VFS automount flags management functions */ diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 80cdffd..8eaa723 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -251,12 +251,6 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) return ino; } -static const struct dentry_operations autofs4_sb_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - int autofs4_fill_super(struct super_block *s, void *data, int silent) { struct inode * root_inode; @@ -311,7 +305,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_iput; pipe = NULL; - root-d_op = autofs4_sb_dentry_operations; + root-d_op = autofs4_dentry_operations; root-d_fsdata = ino; /* Can this call block? */ @@ -322,8 +316,10 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) goto fail_dput; } - if (autofs_type_trigger(sbi-type)) + if (autofs_type_trigger(sbi-type)) { + root-d_op = autofs4_mount_dentry_operations; __managed_dentry_set_managed(root); + } root_inode-i_fop = autofs4_root_operations; root_inode-i_op = autofs4_dir_inode_operations; diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 2856a0c..cb168ba 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -31,6 +31,8 @@ static long autofs4_root_ioctl(struct file *,unsigned int,unsigned long); static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); +static struct vfsmount *autofs4_d_automount(struct path *); +static int autofs4_d_manage(struct path *, bool); const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, @@ -60,6 +62,18 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; +/* For dentries that don't initiate mounting */ +const struct dentry_operations autofs4_dentry_operations = { + .d_release = autofs4_dentry_release, +}; + +/* For dentries that do initiate mounting */ +const struct dentry_operations autofs4_mount_dentry_operations = { + .d_automount= autofs4_d_automount, + .d_manage = autofs4_d_manage, + .d_release = autofs4_dentry_release, +}; + static void autofs4_add_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -151,18 +165,6 @@ void autofs4_dentry_release(struct dentry *de) } } -/* For dentries of directories in the root dir */ -static const struct dentry_operations autofs4_root_dentry_operations = { - .d_release = autofs4_dentry_release, -}; - -/* For other dentries */ -static const struct dentry_operations autofs4_dentry_operations = { - .d_automount= autofs4_d_automount, - .d_manage = autofs4_d_manage, - .d_release = autofs4_dentry_release, -}; - static struct dentry *autofs4_lookup_active(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb); @@ -330,7 +332,7 @@ static struct dentry *autofs4_mountpoint_changed(struct path *path) return path-dentry; } -struct vfsmount *autofs4_d_automount(struct path *path) +static struct vfsmount *autofs4_d_automount(struct path *path) { struct dentry *dentry = path-dentry; struct autofs_sb_info *sbi = autofs4_sbi(dentry-d_sb
Re: [autofs] [PATCH 1/7] autofs4: Save autofs trigger's vfsmount in super block info
Al Viro v...@zeniv.linux.org.uk wrote: AFS can't be arsed and just sets MNT_SHRINKABLE, all flags on parent be damned. Why's that wrong? This is AFS's automounter, and it seems quite reasonable to have the outer edges pruned to make space. David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
Re: [autofs] [PATCH 1/7] autofs4: Save autofs trigger's vfsmount in super block info
Al Viro v...@zeniv.linux.org.uk wrote: Do you want it to inerit e.g. nosuid? Are you just talking about MNT_SHRINKABLE? Or all the other flags? Should I be passing: nd-path.mnt-mnt_flags | MNT_SHRINKABLE instead? David ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 16/59] CRED: Wrap task credential accesses in the autofs filesystem
Wrap access to task credentials so that they can be separated more easily from the task_struct during the introduction of COW creds. Change most current-(|e|s|fs)[ug]id to current_(|e|s|fs)[ug]id(). Change some task-e?[ug]id to task_e?[ug]id(). In some places it makes more sense to use RCU directly rather than a convenient wrapper; these will be addressed by later patches. Signed-off-by: David Howells [EMAIL PROTECTED] Reviewed-by: James Morris [EMAIL PROTECTED] Acked-by: Serge Hallyn [EMAIL PROTECTED] Cc: H. Peter Anvin [EMAIL PROTECTED] Cc: autofs@linux.kernel.org --- fs/autofs/inode.c |4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index dda510d..61429c5 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -76,8 +76,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, substring_t args[MAX_OPT_ARGS]; int option; - *uid = current-uid; - *gid = current-gid; + *uid = current_uid(); + *gid = current_gid(); *pgrp = task_pgrp_nr(current); *minproto = *maxproto = AUTOFS_PROTO_VERSION; ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] [PATCH 17/59] CRED: Wrap task credential accesses in the autofs4 filesystem
Wrap access to task credentials so that they can be separated more easily from the task_struct during the introduction of COW creds. Change most current-(|e|s|fs)[ug]id to current_(|e|s|fs)[ug]id(). Change some task-e?[ug]id to task_e?[ug]id(). In some places it makes more sense to use RCU directly rather than a convenient wrapper; these will be addressed by later patches. Signed-off-by: David Howells [EMAIL PROTECTED] Reviewed-by: James Morris [EMAIL PROTECTED] Acked-by: Serge Hallyn [EMAIL PROTECTED] Cc: Ian Kent [EMAIL PROTECTED] Cc: autofs@linux.kernel.org --- fs/autofs4/inode.c |4 ++-- fs/autofs4/waitq.c |4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 7bb3e5b..8c9cf24 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -233,8 +233,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, substring_t args[MAX_OPT_ARGS]; int option; - *uid = current-uid; - *gid = current-gid; + *uid = current_uid(); + *gid = current_gid(); *pgrp = task_pgrp_nr(current); *minproto = AUTOFS_MIN_PROTO_VERSION; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 35216d1..dc1fa39 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -391,8 +391,8 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, 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-uid = current_uid(); + wq-gid = current_gid(); wq-pid = current-pid; wq-tgid = current-tgid; wq-status = -EINTR; /* Status return if interrupted */ ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs
[autofs] Autofs vs shrink_dcache_for_umount() problem
Andrew Morton [EMAIL PROTECTED] wrote: But why was yum (or an RPM script) dinking with loop and mounting/unmounting things? It may have restarted the automounter... this may cause mounts and unmounts... I've done a bit of probing, and when both ext3 and NFS go into the superblock squidger with a tree that only has a root dentry, that root dentry has a usage count of 1: ### Shrink root c629b088{1} --shrink_dcache_for_umount_subtree(c629b088) - AT c629b088{0} - consume c629b088{0} ### Shrunk ### Shrink root c621d710{1} --shrink_dcache_for_umount_subtree(c621d710) - AT c621d710{0} - consume c621d710{0} ### Shrunk But when autofs4 goes in there with a minimal tree: ### Shrink root c6b714b0{2} --shrink_dcache_for_umount_subtree(c6b714b0) - AT c6b714b0{1} - consume c6b714b0{1} BUG: Dentry c6b714b0{i=1a80,n=/} still in use (1) [unmount of autofs autofs] [ cut here ] kernel BUG at fs/dcache.c:618! It's got a usage count of *2*. That means the root dentry is still in use somewhere beyond the superblock s_root reference. I think this in autofs4_fill_super() is the problem: /* * 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); autofs4 violates the assumptions that I was told I could make. autofs4 expects to clean up its dentries in autofs4_put_super(), when it should perhaps really be overloading the kill_sb() op and doing it before calling kill_anon_super(). I've attached the pair of debugging patches I used to generate the above. David diff --git a/fs/dcache.c b/fs/dcache.c index 4fddd12..ae041da 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -597,7 +597,20 @@ static void shrink_dcache_for_umount_sub do { struct inode *inode; - BUG_ON(atomic_read(dentry-d_count) != 0); + if (atomic_read(dentry-d_count) != 0) { + printk(KERN_ERR + BUG: Dentry %p{i=%lx,n=%s} + still in use (%d) + [unmount of %s %s]\n, + dentry, + dentry-d_inode ? + dentry-d_inode-i_ino : 0UL, + dentry-d_name.name, + atomic_read(dentry-d_count), + dentry-d_sb-s_type-name, + dentry-d_sb-s_id); + BUG(); + } parent = dentry-d_parent; if (parent == dentry) diff --git a/fs/dcache.c b/fs/dcache.c index ae041da..61159d2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -558,6 +558,8 @@ static void shrink_dcache_for_umount_sub BUG_ON(!IS_ROOT(dentry)); + printk(--shrink_dcache_for_umount_subtree(%p)\n, dentry); + /* detach this root from the system */ spin_lock(dcache_lock); if (!list_empty(dentry-d_lru)) { @@ -568,6 +570,8 @@ static void shrink_dcache_for_umount_sub spin_unlock(dcache_lock); for (;;) { + printk(- AT %p{%d}\n, dentry, atomic_read(dentry-d_count)); + /* descend to the first leaf in the current subtree */ while (!list_empty(dentry-d_subdirs)) { struct dentry *loop; @@ -590,6 +594,8 @@ static void shrink_dcache_for_umount_sub /* move to the first child */ dentry = list_entry(dentry-d_subdirs.next, struct dentry, d_u.d_child); + + printk(- descend %p{%d}\n, dentry, atomic_read(dentry-d_count)); } /* consume the dentries from this leaf up through its parents @@ -643,6 +649,7 @@ #endif return; dentry = parent; + printk(- ascend %p{%d}\n, dentry, atomic_read(dentry-d_count)); } while (list_empty(dentry-d_subdirs)); @@ -671,13 +678,17 @@ void shrink_dcache_for_umount(struct sup dentry = sb-s_root; sb-s_root = NULL; + printk(### Shrink root %p{%d}\n, dentry, atomic_read(dentry-d_count)); atomic_dec(dentry-d_count); shrink_dcache_for_umount_subtree(dentry); while (!hlist_empty(sb-s_anon)) { + printk(### Shrink anon\n); dentry = hlist_entry(sb-s_anon.first, struct dentry, d_hash); shrink_dcache_for_umount_subtree(dentry); } + + printk(### Shrunk\n); } /*
[autofs] [PATCH 1/2] VFS: Destroy the dentries contributed by a superblock on unmounting
From: David Howells [EMAIL PROTECTED] The attached patch destroys all the dentries attached to a superblock in one go by: (1) Destroying the tree rooted at s_root. (2) Destroying every entry in the anon list, one at a time. (3) Each entry in the anon list has its subtree consumed from the leaves inwards. This reduces the amount of work generic_shutdown_super() does, and avoids iterating through the dentry_unused list. Note that locking is almost entirely absent in the shrink_dcache_for_umount*() functions added by this patch. This is because: (1) at the point the filesystem calls generic_shutdown_super(), it is not permitted to further touch the superblock's set of dentries, and nor may it remove aliases from inodes; (2) the dcache memory shrinker now skips dentries that are being unmounted; and (3) the superblock no longer has any external references through which the VFS can reach it. Given these points, the only locking we need to do is when we remove dentries from the unused list and the name hashes, which we do a directory's worth at a time. We also don't need to guard against reference counts going to zero unexpectedly and removing bits of the tree we're working on as nothing else can call dput(). A cut down version of dentry_iput() has been folded into shrink_dcache_for_umount_subtree() function. Apart from not needing to unlock things, it also doesn't need to check for inotify watches. In this version of the patch, the complaint about a dentry still being in use has been expanded from a single BUG_ON() and now gives much more information. Signed-Off-By: David Howells [EMAIL PROTECTED] Acked-by: NeilBrown [EMAIL PROTECTED] --- fs/dcache.c| 133 fs/super.c | 12 ++-- include/linux/dcache.h |1 3 files changed, 140 insertions(+), 6 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 48b44a7..ae041da 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -548,6 +548,139 @@ repeat: } /* + * destroy a single subtree of dentries for unmount + * - see the comments on shrink_dcache_for_umount() for a description of the + * locking + */ +static void shrink_dcache_for_umount_subtree(struct dentry *dentry) +{ + struct dentry *parent; + + BUG_ON(!IS_ROOT(dentry)); + + /* detach this root from the system */ + spin_lock(dcache_lock); + if (!list_empty(dentry-d_lru)) { + dentry_stat.nr_unused--; + list_del_init(dentry-d_lru); + } + __d_drop(dentry); + spin_unlock(dcache_lock); + + for (;;) { + /* descend to the first leaf in the current subtree */ + while (!list_empty(dentry-d_subdirs)) { + struct dentry *loop; + + /* this is a branch with children - detach all of them +* from the system in one go */ + spin_lock(dcache_lock); + list_for_each_entry(loop, dentry-d_subdirs, + d_u.d_child) { + if (!list_empty(loop-d_lru)) { + dentry_stat.nr_unused--; + list_del_init(loop-d_lru); + } + + __d_drop(loop); + cond_resched_lock(dcache_lock); + } + spin_unlock(dcache_lock); + + /* move to the first child */ + dentry = list_entry(dentry-d_subdirs.next, + struct dentry, d_u.d_child); + } + + /* consume the dentries from this leaf up through its parents +* until we find one with children or run out altogether */ + do { + struct inode *inode; + + if (atomic_read(dentry-d_count) != 0) { + printk(KERN_ERR + BUG: Dentry %p{i=%lx,n=%s} + still in use (%d) + [unmount of %s %s]\n, + dentry, + dentry-d_inode ? + dentry-d_inode-i_ino : 0UL, + dentry-d_name.name, + atomic_read(dentry-d_count), + dentry-d_sb-s_type-name, + dentry-d_sb-s_id); + BUG(); + } + + parent = dentry-d_parent; + if (parent == dentry) + parent = NULL; + else + atomic_dec(parent-d_count
[autofs] [PATCH 2/2] AUTOFS: Make sure all dentries refs are released before calling kill_anon_super()
From: David Howells [EMAIL PROTECTED] Make sure all dentries refs are released before calling kill_anon_super() so that the assumption that generic_shutdown_super() can completely destroy the dentry tree for there will be no external references holds true. What was being done in the put_super() superblock op, is now done in the kill_sb() filesystem op instead, prior to calling kill_anon_super(). This makes the struct autofs_sb_info::root member variable redundant (since sb-s_root is still available), and so that is removed. The calls to shrink_dcache_sb() are also removed since they're also redundant as shrink_dcache_for_umount() will now be called after the cleanup routine. Signed-Off-By: David Howells [EMAIL PROTECTED] --- fs/autofs4/autofs_i.h |3 +-- fs/autofs4/init.c |2 +- fs/autofs4/inode.c| 22 -- fs/autofs4/waitq.c|1 - 4 files changed, 6 insertions(+), 22 deletions(-) diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index d6603d0..47e38f3 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -96,7 +96,6 @@ #define AUTOFS_TYPE_OFFSET 0x0004 struct autofs_sb_info { u32 magic; - struct dentry *root; int pipefd; struct file *pipe; pid_t oz_pgrp; @@ -231,4 +230,4 @@ out: } void autofs4_dentry_release(struct dentry *); - +extern void autofs4_kill_sb(struct super_block *); diff --git a/fs/autofs4/init.c b/fs/autofs4/init.c index 5d91933..723a1c5 100644 --- a/fs/autofs4/init.c +++ b/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) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index fde78b1..1bf68c5 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -95,7 +95,7 @@ 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; spin_lock(dcache_lock); @@ -126,7 +126,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,15 +139,9 @@ 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); @@ -162,6 +156,7 @@ static void autofs4_put_super(struct sup kfree(sbi); DPRINTK(shutting down); + kill_anon_super(sb); } static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt) @@ -188,7 +183,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,7 +308,6 @@ 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-exp_timeout = 0; @@ -396,13 +389,6 @@ int autofs4_fill_super(struct super_bloc 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); - - /* * Success! Install the root dentry now to indicate completion. */ s-s_root = root; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index ce103e7..c0a6c8d 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -45,7 +45,6 @@ void autofs4_catatonic_mode(struct autof fput(sbi-pipe);/* Close the pipe */ sbi-pipe = NULL; } - shrink_dcache_sb(sbi-sb); } static int autofs4_write(struct file *file, const void *addr, int bytes) ___ autofs mailing list autofs@linux.kernel.org http://linux.kernel.org/mailman/listinfo/autofs