Commit ea5b778a8b98c85a87d66bf844904f9c3802b869

Authored by David Howells
Committed by Al Viro
1 parent ab90911ff9

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>

Showing 8 changed files with 101 additions and 89 deletions Side-by-side Diff

Documentation/filesystems/vfs.txt
... ... @@ -933,15 +933,20 @@
933 933 dynamic_dname() helper function is provided to take care of this.
934 934  
935 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
937   - and return the record to the caller. The caller is supplied with a
938   - path parameter giving the automount directory to describe the automount
939   - target and the parent VFS mount record to provide inheritable mount
940   - parameters. NULL should be returned if someone else managed to make
941   - the automount first. If the automount failed, then an error code
942   - should be returned. If -EISDIR is returned, then the directory will
943   - be treated as an ordinary directory and returned to pathwalk to
944   - continue walking.
  936 + This should create a new VFS mount record and return the record to the
  937 + caller. The caller is supplied with a path parameter giving the
  938 + automount directory to describe the automount target and the parent
  939 + VFS mount record to provide inheritable mount parameters. NULL should
  940 + be returned if someone else managed to make the automount first. If
  941 + the vfsmount creation failed, then an error code should be returned.
  942 + If -EISDIR is returned, then the directory will be treated as an
  943 + ordinary directory and returned to pathwalk to 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 951 This function is only used if DCACHE_NEED_AUTOMOUNT is set on the
947 952 dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the
... ... @@ -241,7 +241,6 @@
241 241 struct vfsmount *afs_d_automount(struct path *path)
242 242 {
243 243 struct vfsmount *newmnt;
244   - int err;
245 244  
246 245 _enter("{%s,%s}", path->mnt->mnt_devname, path->dentry->d_name.name);
247 246  
... ... @@ -249,24 +248,12 @@
249 248 if (IS_ERR(newmnt))
250 249 return newmnt;
251 250  
252   - mntget(newmnt);
253   - err = do_add_mount(newmnt, path, MNT_SHRINKABLE, &afs_vfsmounts);
254   - switch (err) {
255   - case 0:
256   - queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
257   - afs_mntpt_expiry_timeout * HZ);
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   - }
  251 + mntget(newmnt); /* prevent immediate expiration */
  252 + mnt_set_expiry(newmnt, &afs_vfsmounts);
  253 + queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
  254 + afs_mntpt_expiry_timeout * HZ);
  255 + _leave(" = %p {%s}", newmnt, newmnt->mnt_devname);
  256 + return newmnt;
270 257 }
271 258  
272 259 /*
fs/cifs/cifs_dfs_ref.c
... ... @@ -351,7 +351,6 @@
351 351 struct vfsmount *cifs_dfs_d_automount(struct path *path)
352 352 {
353 353 struct vfsmount *newmnt;
354   - int err;
355 354  
356 355 cFYI(1, "in %s", __func__);
357 356  
... ... @@ -361,25 +360,12 @@
361 360 return newmnt;
362 361 }
363 362  
364   - mntget(newmnt);
365   - err = do_add_mount(newmnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
366   - &cifs_dfs_automount_list);
367   - switch (err) {
368   - case 0:
369   - schedule_delayed_work(&cifs_dfs_automount_task,
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   - }
  363 + mntget(newmnt); /* prevent immediate expiration */
  364 + mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
  365 + schedule_delayed_work(&cifs_dfs_automount_task,
  366 + cifs_dfs_mountpoint_expiry_timeout);
  367 + cFYI(1, "leaving %s [ok]" , __func__);
  368 + return newmnt;
383 369 }
384 370  
385 371 const struct inode_operations cifs_dfs_referral_inode_operations = {
... ... @@ -70,6 +70,8 @@
70 70 extern void release_mounts(struct list_head *);
71 71 extern void umount_tree(struct vfsmount *, int, struct list_head *);
72 72 extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
  73 +extern int do_add_mount(struct vfsmount *, struct path *, int);
  74 +extern void mnt_clear_expiry(struct vfsmount *);
73 75  
74 76 extern void __init mnt_init(void);
75 77  
... ... @@ -900,6 +900,7 @@
900 900 bool *need_mntput)
901 901 {
902 902 struct vfsmount *mnt;
  903 + int err;
903 904  
904 905 if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
905 906 return -EREMOTE;
906 907  
907 908  
908 909  
909 910  
... ... @@ -942,22 +943,49 @@
942 943 return -EREMOTE;
943 944 return PTR_ERR(mnt);
944 945 }
  946 +
945 947 if (!mnt) /* mount collision */
946 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 955 if (mnt->mnt_sb == path->mnt->mnt_sb &&
949 956 mnt->mnt_root == path->dentry) {
  957 + mnt_clear_expiry(mnt);
950 958 mntput(mnt);
  959 + mntput(mnt);
951 960 return -ELOOP;
952 961 }
953 962  
954   - dput(path->dentry);
955   - if (*need_mntput)
956   - mntput(path->mnt);
957   - path->mnt = mnt;
958   - path->dentry = dget(mnt->mnt_root);
959   - *need_mntput = true;
960   - return 0;
  963 + /* We need to add the mountpoint to the parent. The filesystem may
  964 + * have placed it on an expiry list, and so we need to make sure it
  965 + * won't be expired under us if do_add_mount() fails (do_add_mount()
  966 + * will eat a reference unconditionally).
  967 + */
  968 + mntget(mnt);
  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 /*
... ... @@ -1925,15 +1925,14 @@
1925 1925 if (IS_ERR(mnt))
1926 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 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 */
1935   -int do_add_mount(struct vfsmount *newmnt, struct path *path,
1936   - int mnt_flags, struct list_head *fslist)
  1935 +int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags)
1937 1936 {
1938 1937 int err;
1939 1938  
... ... @@ -1963,9 +1962,6 @@
1963 1962 if ((err = graft_tree(newmnt, path)))
1964 1963 goto unlock;
1965 1964  
1966   - if (fslist) /* add to the specified expiration list */
1967   - list_add_tail(&newmnt->mnt_expire, fslist);
1968   -
1969 1965 up_write(&namespace_sem);
1970 1966 return 0;
1971 1967  
... ... @@ -1975,7 +1971,36 @@
1975 1971 return err;
1976 1972 }
1977 1973  
1978   -EXPORT_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 + */
  1979 +void 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 +}
  1989 +EXPORT_SYMBOL(mnt_set_expiry);
  1990 +
  1991 +/*
  1992 + * Remove a vfsmount from any expiration list it may be on
  1993 + */
  1994 +void 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 2006 * process a list of expirable mountpoints with the intent of discarding any
... ... @@ -149,26 +149,10 @@
149 149 if (IS_ERR(mnt))
150 150 goto out;
151 151  
152   - mntget(mnt);
153   - err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE,
154   - &nfs_automount_list);
155   - switch (err) {
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   - }
  152 + dprintk("%s: done, success\n", __func__);
  153 + mntget(mnt); /* prevent immediate expiration */
  154 + mnt_set_expiry(mnt, &nfs_automount_list);
  155 + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
172 156  
173 157 out:
174 158 nfs_free_fattr(fattr);
include/linux/mount.h
... ... @@ -110,12 +110,7 @@
110 110 int flags, const char *name,
111 111 void *data);
112 112  
113   -struct nameidata;
114   -
115   -struct path;
116   -extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
117   - int mnt_flags, struct list_head *fslist);
118   -
  113 +extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
119 114 extern void mark_mounts_for_expiry(struct list_head *mounts);
120 115  
121 116 extern dev_t name_to_dev_t(char *name);