diff options
-rw-r--r-- | Documentation/filesystems/vfs.txt | 23 | ||||
-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 | ||||
-rw-r--r-- | 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 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: | |||
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); |
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 | ||
113 | struct nameidata; | 113 | extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list); |
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 | |||
119 | extern void mark_mounts_for_expiry(struct list_head *mounts); | 114 | extern void mark_mounts_for_expiry(struct list_head *mounts); |
120 | 115 | ||
121 | extern dev_t name_to_dev_t(char *name); | 116 | extern dev_t name_to_dev_t(char *name); |