aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-01-14 14:10:03 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2011-01-15 20:07:48 -0500
commitea5b778a8b98c85a87d66bf844904f9c3802b869 (patch)
treebaa56cbe1a907d76341f2cad53e16569cc1d3288
parentab90911ff90cdab59b31c045c3f0ae480d14f29d (diff)
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. 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 to propagate the mount flags from the parent vfsmount. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--Documentation/filesystems/vfs.txt23
-rw-r--r--fs/afs/mntpt.c25
-rw-r--r--fs/cifs/cifs_dfs_ref.c26
-rw-r--r--fs/internal.h2
-rw-r--r--fs/namei.c42
-rw-r--r--fs/namespace.c41
-rw-r--r--fs/nfs/namespace.c24
-rw-r--r--include/linux/mount.h7
8 files changed, 101 insertions, 89 deletions
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 3c4b2f1b64d0..94cf97b901d7 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -933,15 +933,20 @@ struct dentry_operations {
933 dynamic_dname() helper function is provided to take care of this. 933 dynamic_dname() helper function is provided to take care of this.
934 934
935 d_automount: called when an automount dentry is to be traversed (optional). 935 d_automount: called when an automount dentry is to be traversed (optional).
936 This should create a new VFS mount record, mount it on the directory 936 This should create a new VFS mount record and return the record to the
937 and return the record to the caller. The caller is supplied with a 937 caller. The caller is supplied with a path parameter giving the
938 path parameter giving the automount directory to describe the automount 938 automount directory to describe the automount target and the parent
939 target and the parent VFS mount record to provide inheritable mount 939 VFS mount record to provide inheritable mount parameters. NULL should
940 parameters. NULL should be returned if someone else managed to make 940 be returned if someone else managed to make the automount first. If
941 the automount first. If the automount failed, then an error code 941 the vfsmount creation failed, then an error code should be returned.
942 should be returned. If -EISDIR is returned, then the directory will 942 If -EISDIR is returned, then the directory will be treated as an
943 be treated as an ordinary directory and returned to pathwalk to 943 ordinary directory and returned to pathwalk to continue walking.
944 continue walking. 944
945 If a vfsmount is returned, the caller will attempt to mount it on the
946 mountpoint and will remove the vfsmount from its expiration list in
947 the case of failure. The vfsmount should be returned with 2 refs on
948 it to prevent automatic expiration - the caller will clean up the
949 additional ref.
945 950
946 This function is only used if DCACHE_NEED_AUTOMOUNT is set on the 951 This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
947 dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the 952 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 d23b2e344a78..aa59184151d0 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -241,7 +241,6 @@ error_no_devname:
241struct vfsmount *afs_d_automount(struct path *path) 241struct vfsmount *afs_d_automount(struct path *path)
242{ 242{
243 struct vfsmount *newmnt; 243 struct vfsmount *newmnt;
244 int err;
245 244
246 _enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name); 245 _enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
247 246
@@ -249,24 +248,12 @@ struct vfsmount *afs_d_automount(struct path *path)
249 if (IS_ERR(newmnt)) 248 if (IS_ERR(newmnt))
250 return newmnt; 249 return newmnt;
251 250
252 mntget(newmnt); 251 mntget(newmnt); /* prevent immediate expiration */
253 err = do_add_mount(newmnt, path, MNT_SHRINKABLE, &afs_vfsmounts); 252 mnt_set_expiry(newmnt, &afs_vfsmounts);
254 switch (err) { 253 queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
255 case 0: 254 afs_mntpt_expiry_timeout * HZ);
256 queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer, 255 _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
257 afs_mntpt_expiry_timeout * HZ); 256 return newmnt;
258 _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
259 return newmnt;
260 case -EBUSY:
261 /* someone else made a mount here whilst we were busy */
262 mntput(newmnt);
263 _leave(" = NULL [EBUSY]");
264 return NULL;
265 default:
266 mntput(newmnt);
267 _leave(" = %d", err);
268 return ERR_PTR(err);
269 }
270} 257}
271 258
272/* 259/*
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 0fc163808de3..7ed36536e754 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -351,7 +351,6 @@ free_xid:
351struct vfsmount *cifs_dfs_d_automount(struct path *path) 351struct vfsmount *cifs_dfs_d_automount(struct path *path)
352{ 352{
353 struct vfsmount *newmnt; 353 struct vfsmount *newmnt;
354 int err;
355 354
356 cFYI(1, "in %s", __func__); 355 cFYI(1, "in %s", __func__);
357 356
@@ -361,25 +360,12 @@ struct vfsmount *cifs_dfs_d_automount(struct path *path)
361 return newmnt; 360 return newmnt;
362 } 361 }
363 362
364 mntget(newmnt); 363 mntget(newmnt); /* prevent immediate expiration */
365 err = do_add_mount(newmnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE, 364 mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
366 &cifs_dfs_automount_list); 365 schedule_delayed_work(&cifs_dfs_automount_task,
367 switch (err) { 366 cifs_dfs_mountpoint_expiry_timeout);
368 case 0: 367 cFYI(1, "leaving %s [ok]" , __func__);
369 schedule_delayed_work(&cifs_dfs_automount_task, 368 return newmnt;
370 cifs_dfs_mountpoint_expiry_timeout);
371 cFYI(1, "leaving %s [ok]" , __func__);
372 return newmnt;
373 case -EBUSY:
374 /* someone else made a mount here whilst we were busy */
375 mntput(newmnt);
376 cFYI(1, "leaving %s [EBUSY]" , __func__);
377 return NULL;
378 default:
379 mntput(newmnt);
380 cFYI(1, "leaving %s [error %d]" , __func__, err);
381 return ERR_PTR(err);
382 }
383} 369}
384 370
385const struct inode_operations cifs_dfs_referral_inode_operations = { 371const struct inode_operations cifs_dfs_referral_inode_operations = {
diff --git a/fs/internal.h b/fs/internal.h
index 9687c2ee2735..4931060fd089 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -70,6 +70,8 @@ extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
70extern void release_mounts(struct list_head *); 70extern void release_mounts(struct list_head *);
71extern void umount_tree(struct vfsmount *, int, struct list_head *); 71extern void umount_tree(struct vfsmount *, int, struct list_head *);
72extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); 72extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
73extern int do_add_mount(struct vfsmount *, struct path *, int);
74extern void mnt_clear_expiry(struct vfsmount *);
73 75
74extern void __init mnt_init(void); 76extern void __init mnt_init(void);
75 77
diff --git a/fs/namei.c b/fs/namei.c
index 5c89695ae1e4..c2e37727e3ab 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -900,6 +900,7 @@ static int follow_automount(struct path *path, unsigned flags,
900 bool *need_mntput) 900 bool *need_mntput)
901{ 901{
902 struct vfsmount *mnt; 902 struct vfsmount *mnt;
903 int err;
903 904
904 if (!path->dentry->d_op || !path->dentry->d_op->d_automount) 905 if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
905 return -EREMOTE; 906 return -EREMOTE;
@@ -942,22 +943,49 @@ static int follow_automount(struct path *path, unsigned flags,
942 return -EREMOTE; 943 return -EREMOTE;
943 return PTR_ERR(mnt); 944 return PTR_ERR(mnt);
944 } 945 }
946
945 if (!mnt) /* mount collision */ 947 if (!mnt) /* mount collision */
946 return 0; 948 return 0;
947 949
950 /* The new mount record should have at least 2 refs to prevent it being
951 * expired before we get a chance to add it
952 */
953 BUG_ON(mnt_get_count(mnt) < 2);
954
948 if (mnt->mnt_sb == path->mnt->mnt_sb && 955 if (mnt->mnt_sb == path->mnt->mnt_sb &&
949 mnt->mnt_root == path->dentry) { 956 mnt->mnt_root == path->dentry) {
957 mnt_clear_expiry(mnt);
958 mntput(mnt);
950 mntput(mnt); 959 mntput(mnt);
951 return -ELOOP; 960 return -ELOOP;
952 } 961 }
953 962
954 dput(path->dentry); 963 /* We need to add the mountpoint to the parent. The filesystem may
955 if (*need_mntput) 964 * have placed it on an expiry list, and so we need to make sure it
956 mntput(path->mnt); 965 * won't be expired under us if do_add_mount() fails (do_add_mount()
957 path->mnt = mnt; 966 * will eat a reference unconditionally).
958 path->dentry = dget(mnt->mnt_root); 967 */
959 *need_mntput = true; 968 mntget(mnt);
960 return 0; 969 err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
970 switch (err) {
971 case -EBUSY:
972 /* Someone else made a mount here whilst we were busy */
973 err = 0;
974 default:
975 mnt_clear_expiry(mnt);
976 mntput(mnt);
977 mntput(mnt);
978 return err;
979 case 0:
980 mntput(mnt);
981 dput(path->dentry);
982 if (*need_mntput)
983 mntput(path->mnt);
984 path->mnt = mnt;
985 path->dentry = dget(mnt->mnt_root);
986 *need_mntput = true;
987 return 0;
988 }
961} 989}
962 990
963/* 991/*
diff --git a/fs/namespace.c b/fs/namespace.c
index d94ccd6ddafd..bfcb701f9490 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1925,15 +1925,14 @@ static int do_new_mount(struct path *path, char *type, int flags,
1925 if (IS_ERR(mnt)) 1925 if (IS_ERR(mnt))
1926 return PTR_ERR(mnt); 1926 return PTR_ERR(mnt);
1927 1927
1928 return do_add_mount(mnt, path, mnt_flags, NULL); 1928 return do_add_mount(mnt, path, mnt_flags);
1929} 1929}
1930 1930
1931/* 1931/*
1932 * add a mount into a namespace's mount tree 1932 * add a mount into a namespace's mount tree
1933 * - provide the option of adding the new mount to an expiration list 1933 * - this unconditionally eats one of the caller's references to newmnt.
1934 */ 1934 */
1935int do_add_mount(struct vfsmount *newmnt, struct path *path, 1935int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
1936 int mnt_flags, struct list_head *fslist)
1937{ 1936{
1938 int err; 1937 int err;
1939 1938
@@ -1963,9 +1962,6 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
1963 if ((err = graft_tree(newmnt, path))) 1962 if ((err = graft_tree(newmnt, path)))
1964 goto unlock; 1963 goto unlock;
1965 1964
1966 if (fslist) /* add to the specified expiration list */
1967 list_add_tail(&newmnt->mnt_expire, fslist);
1968
1969 up_write(&namespace_sem); 1965 up_write(&namespace_sem);
1970 return 0; 1966 return 0;
1971 1967
@@ -1975,7 +1971,36 @@ unlock:
1975 return err; 1971 return err;
1976} 1972}
1977 1973
1978EXPORT_SYMBOL_GPL(do_add_mount); 1974/**
1975 * mnt_set_expiry - Put a mount on an expiration list
1976 * @mnt: The mount to list.
1977 * @expiry_list: The list to add the mount to.
1978 */
1979void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
1980{
1981 down_write(&namespace_sem);
1982 br_write_lock(vfsmount_lock);
1983
1984 list_add_tail(&mnt->mnt_expire, expiry_list);
1985
1986 br_write_unlock(vfsmount_lock);
1987 up_write(&namespace_sem);
1988}
1989EXPORT_SYMBOL(mnt_set_expiry);
1990
1991/*
1992 * Remove a vfsmount from any expiration list it may be on
1993 */
1994void mnt_clear_expiry(struct vfsmount *mnt)
1995{
1996 if (!list_empty(&mnt->mnt_expire)) {
1997 down_write(&namespace_sem);
1998 br_write_lock(vfsmount_lock);
1999 list_del_init(&mnt->mnt_expire);
2000 br_write_unlock(vfsmount_lock);
2001 up_write(&namespace_sem);
2002 }
2003}
1979 2004
1980/* 2005/*
1981 * process a list of expirable mountpoints with the intent of discarding any 2006 * process a list of expirable mountpoints with the intent of discarding any
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index f3fbb1bf3f18..f32b8603dca8 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -149,26 +149,10 @@ struct vfsmount *nfs_d_automount(struct path *path)
149 if (IS_ERR(mnt)) 149 if (IS_ERR(mnt))
150 goto out; 150 goto out;
151 151
152 mntget(mnt); 152 dprintk("%s: done, success\n", __func__);
153 err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE, 153 mntget(mnt); /* prevent immediate expiration */
154 &nfs_automount_list); 154 mnt_set_expiry(mnt, &nfs_automount_list);
155 switch (err) { 155 schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
156 case 0:
157 dprintk("%s: done, success\n", __func__);
158 schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
159 break;
160 case -EBUSY:
161 /* someone else made a mount here whilst we were busy */
162 mntput(mnt);
163 dprintk("%s: done, collision\n", __func__);
164 mnt = NULL;
165 break;
166 default:
167 mntput(mnt);
168 dprintk("%s: done, error %d\n", __func__, err);
169 mnt = ERR_PTR(err);
170 break;
171 }
172 156
173out: 157out:
174 nfs_free_fattr(fattr); 158 nfs_free_fattr(fattr);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 1869ea24a739..af4765ea8fde 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -110,12 +110,7 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
110 int flags, const char *name, 110 int flags, const char *name,
111 void *data); 111 void *data);
112 112
113struct nameidata; 113extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
114
115struct path;
116extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
117 int mnt_flags, struct list_head *fslist);
118
119extern void mark_mounts_for_expiry(struct list_head *mounts); 114extern void mark_mounts_for_expiry(struct list_head *mounts);
120 115
121extern dev_t name_to_dev_t(char *name); 116extern dev_t name_to_dev_t(char *name);