aboutsummaryrefslogtreecommitdiffstats
path: root/fs
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 /fs
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>
Diffstat (limited to 'fs')
-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
6 files changed, 86 insertions, 74 deletions
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);