diff options
author | David Howells <dhowells@redhat.com> | 2011-01-14 14:10:03 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-15 20:07:48 -0500 |
commit | ea5b778a8b98c85a87d66bf844904f9c3802b869 (patch) | |
tree | baa56cbe1a907d76341f2cad53e16569cc1d3288 /fs | |
parent | ab90911ff90cdab59b31c045c3f0ae480d14f29d (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.c | 25 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 26 | ||||
-rw-r--r-- | fs/internal.h | 2 | ||||
-rw-r--r-- | fs/namei.c | 42 | ||||
-rw-r--r-- | fs/namespace.c | 41 | ||||
-rw-r--r-- | fs/nfs/namespace.c | 24 |
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: | |||
241 | struct vfsmount *afs_d_automount(struct path *path) | 241 | struct 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: | |||
351 | struct vfsmount *cifs_dfs_d_automount(struct path *path) | 351 | struct 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 | ||
385 | const struct inode_operations cifs_dfs_referral_inode_operations = { | 371 | const 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 *, | |||
70 | extern void release_mounts(struct list_head *); | 70 | extern void release_mounts(struct list_head *); |
71 | extern void umount_tree(struct vfsmount *, int, struct list_head *); | 71 | extern void umount_tree(struct vfsmount *, int, struct list_head *); |
72 | extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); | 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 | extern void __init mnt_init(void); | 76 | extern 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 | */ |
1935 | int do_add_mount(struct vfsmount *newmnt, struct path *path, | 1935 | int 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 | ||
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 | * 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 | ||
173 | out: | 157 | out: |
174 | nfs_free_fattr(fattr); | 158 | nfs_free_fattr(fattr); |