diff options
author | David Howells <dhowells@redhat.com> | 2011-01-14 13:45:42 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-15 20:07:34 -0500 |
commit | 36d43a43761b004ad1879ac21471d8fc5f3157ec (patch) | |
tree | 6cb6c6d978f4e58de7f9bf901707d6929f098345 | |
parent | d18610b0ce9eb48c60649d8fcbf68374c84349d3 (diff) |
NFS: Use d_automount() rather than abusing follow_link()
Make NFS use the new d_automount() dentry operation rather than abusing
follow_link() on directories.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: Ian Kent <raven@themaw.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/nfs/dir.c | 4 | ||||
-rw-r--r-- | fs/nfs/inode.c | 4 | ||||
-rw-r--r-- | fs/nfs/internal.h | 1 | ||||
-rw-r--r-- | fs/nfs/namespace.c | 84 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 |
5 files changed, 46 insertions, 48 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index df8c03a02161..2c3eb33b904d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -970,7 +970,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) | |||
970 | { | 970 | { |
971 | struct nfs_server *server = NFS_SERVER(inode); | 971 | struct nfs_server *server = NFS_SERVER(inode); |
972 | 972 | ||
973 | if (test_bit(NFS_INO_MOUNTPOINT, &NFS_I(inode)->flags)) | 973 | if (IS_AUTOMOUNT(inode)) |
974 | return 0; | 974 | return 0; |
975 | if (nd != NULL) { | 975 | if (nd != NULL) { |
976 | /* VFS wants an on-the-wire revalidation */ | 976 | /* VFS wants an on-the-wire revalidation */ |
@@ -1173,6 +1173,7 @@ const struct dentry_operations nfs_dentry_operations = { | |||
1173 | .d_revalidate = nfs_lookup_revalidate, | 1173 | .d_revalidate = nfs_lookup_revalidate, |
1174 | .d_delete = nfs_dentry_delete, | 1174 | .d_delete = nfs_dentry_delete, |
1175 | .d_iput = nfs_dentry_iput, | 1175 | .d_iput = nfs_dentry_iput, |
1176 | .d_automount = nfs_d_automount, | ||
1176 | }; | 1177 | }; |
1177 | 1178 | ||
1178 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) | 1179 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) |
@@ -1246,6 +1247,7 @@ const struct dentry_operations nfs4_dentry_operations = { | |||
1246 | .d_revalidate = nfs_open_revalidate, | 1247 | .d_revalidate = nfs_open_revalidate, |
1247 | .d_delete = nfs_dentry_delete, | 1248 | .d_delete = nfs_dentry_delete, |
1248 | .d_iput = nfs_dentry_iput, | 1249 | .d_iput = nfs_dentry_iput, |
1250 | .d_automount = nfs_d_automount, | ||
1249 | }; | 1251 | }; |
1250 | 1252 | ||
1251 | /* | 1253 | /* |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ce00b704452c..d8512423ba72 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -300,7 +300,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) | |||
300 | else | 300 | else |
301 | inode->i_op = &nfs_mountpoint_inode_operations; | 301 | inode->i_op = &nfs_mountpoint_inode_operations; |
302 | inode->i_fop = NULL; | 302 | inode->i_fop = NULL; |
303 | set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags); | 303 | inode->i_flags |= S_AUTOMOUNT; |
304 | } | 304 | } |
305 | } else if (S_ISLNK(inode->i_mode)) | 305 | } else if (S_ISLNK(inode->i_mode)) |
306 | inode->i_op = &nfs_symlink_inode_operations; | 306 | inode->i_op = &nfs_symlink_inode_operations; |
@@ -1208,7 +1208,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | |||
1208 | /* Update the fsid? */ | 1208 | /* Update the fsid? */ |
1209 | if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) && | 1209 | if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) && |
1210 | !nfs_fsid_equal(&server->fsid, &fattr->fsid) && | 1210 | !nfs_fsid_equal(&server->fsid, &fattr->fsid) && |
1211 | !test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags)) | 1211 | !IS_AUTOMOUNT(inode)) |
1212 | server->fsid = fattr->fsid; | 1212 | server->fsid = fattr->fsid; |
1213 | 1213 | ||
1214 | /* | 1214 | /* |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index bfa3a34af801..4644f04b4b46 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -252,6 +252,7 @@ extern char *nfs_path(const char *base, | |||
252 | const struct dentry *droot, | 252 | const struct dentry *droot, |
253 | const struct dentry *dentry, | 253 | const struct dentry *dentry, |
254 | char *buffer, ssize_t buflen); | 254 | char *buffer, ssize_t buflen); |
255 | extern struct vfsmount *nfs_d_automount(struct path *path); | ||
255 | 256 | ||
256 | /* getroot.c */ | 257 | /* getroot.c */ |
257 | extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); | 258 | extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); |
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index bfcb933e5755..f3fbb1bf3f18 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
@@ -97,9 +97,8 @@ Elong: | |||
97 | } | 97 | } |
98 | 98 | ||
99 | /* | 99 | /* |
100 | * nfs_follow_mountpoint - handle crossing a mountpoint on the server | 100 | * nfs_d_automount - Handle crossing a mountpoint on the server |
101 | * @dentry - dentry of mountpoint | 101 | * @path - The mountpoint |
102 | * @nd - nameidata info | ||
103 | * | 102 | * |
104 | * When we encounter a mountpoint on the server, we want to set up | 103 | * When we encounter a mountpoint on the server, we want to set up |
105 | * a mountpoint on the client too, to prevent inode numbers from | 104 | * a mountpoint on the client too, to prevent inode numbers from |
@@ -109,84 +108,81 @@ Elong: | |||
109 | * situation, and that different filesystems may want to use | 108 | * situation, and that different filesystems may want to use |
110 | * different security flavours. | 109 | * different security flavours. |
111 | */ | 110 | */ |
112 | static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | 111 | struct vfsmount *nfs_d_automount(struct path *path) |
113 | { | 112 | { |
114 | struct vfsmount *mnt; | 113 | struct vfsmount *mnt; |
115 | struct nfs_server *server = NFS_SERVER(dentry->d_inode); | 114 | struct nfs_server *server = NFS_SERVER(path->dentry->d_inode); |
116 | struct dentry *parent; | 115 | struct dentry *parent; |
117 | struct nfs_fh *fh = NULL; | 116 | struct nfs_fh *fh = NULL; |
118 | struct nfs_fattr *fattr = NULL; | 117 | struct nfs_fattr *fattr = NULL; |
119 | int err; | 118 | int err; |
120 | 119 | ||
121 | dprintk("--> nfs_follow_mountpoint()\n"); | 120 | dprintk("--> nfs_d_automount()\n"); |
122 | 121 | ||
123 | err = -ESTALE; | 122 | mnt = ERR_PTR(-ESTALE); |
124 | if (IS_ROOT(dentry)) | 123 | if (IS_ROOT(path->dentry)) |
125 | goto out_err; | 124 | goto out_nofree; |
126 | 125 | ||
127 | err = -ENOMEM; | 126 | mnt = ERR_PTR(-ENOMEM); |
128 | fh = nfs_alloc_fhandle(); | 127 | fh = nfs_alloc_fhandle(); |
129 | fattr = nfs_alloc_fattr(); | 128 | fattr = nfs_alloc_fattr(); |
130 | if (fh == NULL || fattr == NULL) | 129 | if (fh == NULL || fattr == NULL) |
131 | goto out_err; | 130 | goto out; |
132 | 131 | ||
133 | dprintk("%s: enter\n", __func__); | 132 | dprintk("%s: enter\n", __func__); |
134 | dput(nd->path.dentry); | ||
135 | nd->path.dentry = dget(dentry); | ||
136 | 133 | ||
137 | /* Look it up again */ | 134 | /* Look it up again to get its attributes */ |
138 | parent = dget_parent(nd->path.dentry); | 135 | parent = dget_parent(path->dentry); |
139 | err = server->nfs_client->rpc_ops->lookup(parent->d_inode, | 136 | err = server->nfs_client->rpc_ops->lookup(parent->d_inode, |
140 | &nd->path.dentry->d_name, | 137 | &path->dentry->d_name, |
141 | fh, fattr); | 138 | fh, fattr); |
142 | dput(parent); | 139 | dput(parent); |
143 | if (err != 0) | 140 | if (err != 0) { |
144 | goto out_err; | 141 | mnt = ERR_PTR(err); |
142 | goto out; | ||
143 | } | ||
145 | 144 | ||
146 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) | 145 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) |
147 | mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); | 146 | mnt = nfs_do_refmount(path->mnt, path->dentry); |
148 | else | 147 | else |
149 | mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, | 148 | mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr); |
150 | fattr); | ||
151 | err = PTR_ERR(mnt); | ||
152 | if (IS_ERR(mnt)) | 149 | if (IS_ERR(mnt)) |
153 | goto out_err; | 150 | goto out; |
154 | 151 | ||
155 | mntget(mnt); | 152 | mntget(mnt); |
156 | err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, | 153 | err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE, |
157 | &nfs_automount_list); | 154 | &nfs_automount_list); |
158 | if (err < 0) { | 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 */ | ||
159 | mntput(mnt); | 162 | mntput(mnt); |
160 | if (err == -EBUSY) | 163 | dprintk("%s: done, collision\n", __func__); |
161 | goto out_follow; | 164 | mnt = NULL; |
162 | goto out_err; | 165 | break; |
166 | default: | ||
167 | mntput(mnt); | ||
168 | dprintk("%s: done, error %d\n", __func__, err); | ||
169 | mnt = ERR_PTR(err); | ||
170 | break; | ||
163 | } | 171 | } |
164 | path_put(&nd->path); | 172 | |
165 | nd->path.mnt = mnt; | ||
166 | nd->path.dentry = dget(mnt->mnt_root); | ||
167 | schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); | ||
168 | out: | 173 | out: |
169 | nfs_free_fattr(fattr); | 174 | nfs_free_fattr(fattr); |
170 | nfs_free_fhandle(fh); | 175 | nfs_free_fhandle(fh); |
171 | dprintk("%s: done, returned %d\n", __func__, err); | 176 | out_nofree: |
172 | 177 | dprintk("<-- nfs_follow_mountpoint() = %p\n", mnt); | |
173 | dprintk("<-- nfs_follow_mountpoint() = %d\n", err); | 178 | return mnt; |
174 | return ERR_PTR(err); | ||
175 | out_err: | ||
176 | path_put(&nd->path); | ||
177 | goto out; | ||
178 | out_follow: | ||
179 | err = follow_down(&nd->path, false); | ||
180 | goto out; | ||
181 | } | 179 | } |
182 | 180 | ||
183 | const struct inode_operations nfs_mountpoint_inode_operations = { | 181 | const struct inode_operations nfs_mountpoint_inode_operations = { |
184 | .follow_link = nfs_follow_mountpoint, | ||
185 | .getattr = nfs_getattr, | 182 | .getattr = nfs_getattr, |
186 | }; | 183 | }; |
187 | 184 | ||
188 | const struct inode_operations nfs_referral_inode_operations = { | 185 | const struct inode_operations nfs_referral_inode_operations = { |
189 | .follow_link = nfs_follow_mountpoint, | ||
190 | }; | 186 | }; |
191 | 187 | ||
192 | static void nfs_expire_automounts(struct work_struct *work) | 188 | static void nfs_expire_automounts(struct work_struct *work) |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0779bb8f95be..6023efa9f5d9 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -215,7 +215,6 @@ struct nfs_inode { | |||
215 | #define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */ | 215 | #define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */ |
216 | #define NFS_INO_STALE (1) /* possible stale inode */ | 216 | #define NFS_INO_STALE (1) /* possible stale inode */ |
217 | #define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */ | 217 | #define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */ |
218 | #define NFS_INO_MOUNTPOINT (3) /* inode is remote mountpoint */ | ||
219 | #define NFS_INO_FLUSHING (4) /* inode is flushing out data */ | 218 | #define NFS_INO_FLUSHING (4) /* inode is flushing out data */ |
220 | #define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */ | 219 | #define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */ |
221 | #define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */ | 220 | #define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */ |