Commit f05d147f7e3cf0d86b3a4bd5603029a7cb109633

Authored by Bryan Schumaker
Committed by Trond Myklebust
1 parent 72de53ec4b

NFS: Fix following referral mount points with different security

I create a new proc_lookup_mountpoint() to use when submounting an NFS
v4 share.  This function returns an rpc_clnt to use for performing an
fs_locations() call on a referral's mountpoint.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

Showing 5 changed files with 72 additions and 26 deletions Side-by-side Diff

... ... @@ -186,10 +186,10 @@
186 186  
187 187 /* nfs4namespace.c */
188 188 #ifdef CONFIG_NFS_V4
189   -extern struct vfsmount *nfs_do_refmount(struct dentry *dentry);
  189 +extern struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry);
190 190 #else
191 191 static inline
192   -struct vfsmount *nfs_do_refmount(struct dentry *dentry)
  192 +struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
193 193 {
194 194 return ERR_PTR(-ENOENT);
195 195 }
... ... @@ -200,6 +200,22 @@
200 200 out:
201 201 return err;
202 202 }
  203 +
  204 +static struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir,
  205 + struct qstr *name,
  206 + struct nfs_fh *fh,
  207 + struct nfs_fattr *fattr)
  208 +{
  209 + int err;
  210 +
  211 + if (NFS_PROTO(dir)->version == 4)
  212 + return nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
  213 +
  214 + err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr);
  215 + if (err)
  216 + return ERR_PTR(err);
  217 + return rpc_clone_client(NFS_SERVER(dir)->client);
  218 +}
203 219 #else /* CONFIG_NFS_V4 */
204 220 static inline int nfs_lookup_with_sec(struct nfs_server *server,
205 221 struct dentry *parent, struct dentry *dentry,
... ... @@ -209,6 +225,17 @@
209 225 {
210 226 return -EPERM;
211 227 }
  228 +
  229 +static inline struct rpc_clnt *nfs_lookup_mountpoint(struct inode *dir,
  230 + struct qstr *name,
  231 + struct nfs_fh *fh,
  232 + struct nfs_fattr *fattr)
  233 +{
  234 + int err = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, name, fh, fattr);
  235 + if (err)
  236 + return ERR_PTR(err);
  237 + return rpc_clone_client(NFS_SERVER(dir)->client);
  238 +}
212 239 #endif /* CONFIG_NFS_V4 */
213 240  
214 241 /*
215 242  
... ... @@ -226,11 +253,10 @@
226 253 struct vfsmount *nfs_d_automount(struct path *path)
227 254 {
228 255 struct vfsmount *mnt;
229   - struct nfs_server *server = NFS_SERVER(path->dentry->d_inode);
230 256 struct dentry *parent;
231 257 struct nfs_fh *fh = NULL;
232 258 struct nfs_fattr *fattr = NULL;
233   - int err;
  259 + struct rpc_clnt *client;
234 260 rpc_authflavor_t flavor = RPC_AUTH_UNIX;
235 261  
236 262 dprintk("--> nfs_d_automount()\n");
237 263  
238 264  
239 265  
... ... @@ -249,21 +275,19 @@
249 275  
250 276 /* Look it up again to get its attributes */
251 277 parent = dget_parent(path->dentry);
252   - err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
253   - &path->dentry->d_name,
254   - fh, fattr);
255   - if (err == -EPERM && NFS_PROTO(parent->d_inode)->secinfo != NULL)
256   - err = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr, &flavor);
  278 + client = nfs_lookup_mountpoint(parent->d_inode, &path->dentry->d_name, fh, fattr);
257 279 dput(parent);
258   - if (err != 0) {
259   - mnt = ERR_PTR(err);
  280 + if (IS_ERR(client)) {
  281 + mnt = ERR_CAST(client);
260 282 goto out;
261 283 }
262 284  
263 285 if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
264   - mnt = nfs_do_refmount(path->dentry);
  286 + mnt = nfs_do_refmount(client, path->dentry);
265 287 else
266 288 mnt = nfs_do_submount(path->dentry, fh, fattr, flavor);
  289 + rpc_shutdown_client(client);
  290 +
267 291 if (IS_ERR(mnt))
268 292 goto out;
269 293  
... ... @@ -216,8 +216,10 @@
216 216 extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
217 217 extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
218 218 extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
219   -extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
220   - struct nfs4_fs_locations *fs_locations, struct page *page);
  219 +extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *,
  220 + struct nfs4_fs_locations *, struct page *);
  221 +extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
  222 + struct nfs_fh *, struct nfs_fattr *);
221 223 extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
222 224 extern int nfs4_release_lockowner(struct nfs4_lock_state *);
223 225 extern const struct xattr_handler *nfs4_xattr_handlers[];
fs/nfs/nfs4namespace.c
... ... @@ -300,7 +300,7 @@
300 300 * @dentry - dentry of referral
301 301 *
302 302 */
303   -struct vfsmount *nfs_do_refmount(struct dentry *dentry)
  303 +struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
304 304 {
305 305 struct vfsmount *mnt = ERR_PTR(-ENOMEM);
306 306 struct dentry *parent;
... ... @@ -326,7 +326,7 @@
326 326 dprintk("%s: getting locations for %s/%s\n",
327 327 __func__, parent->d_name.name, dentry->d_name.name);
328 328  
329   - err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page);
  329 + err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page);
330 330 dput(parent);
331 331 if (err != 0 ||
332 332 fs_locations->nlocations <= 0 ||
... ... @@ -2377,8 +2377,9 @@
2377 2377 * Note that we'll actually follow the referral later when
2378 2378 * we detect fsid mismatch in inode revalidation
2379 2379 */
2380   -static int nfs4_get_referral(struct inode *dir, const struct qstr *name,
2381   - struct nfs_fattr *fattr, struct nfs_fh *fhandle)
  2380 +static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
  2381 + const struct qstr *name, struct nfs_fattr *fattr,
  2382 + struct nfs_fh *fhandle)
2382 2383 {
2383 2384 int status = -ENOMEM;
2384 2385 struct page *page = NULL;
... ... @@ -2391,7 +2392,7 @@
2391 2392 if (locations == NULL)
2392 2393 goto out;
2393 2394  
2394   - status = nfs4_proc_fs_locations(dir, name, locations, page);
  2395 + status = nfs4_proc_fs_locations(client, dir, name, locations, page);
2395 2396 if (status != 0)
2396 2397 goto out;
2397 2398 /* Make sure server returned a different fsid for the referral */
... ... @@ -2550,7 +2551,7 @@
2550 2551 err = -ENOENT;
2551 2552 goto out;
2552 2553 case -NFS4ERR_MOVED:
2553   - err = nfs4_get_referral(dir, name, fattr, fhandle);
  2554 + err = nfs4_get_referral(client, dir, name, fattr, fhandle);
2554 2555 goto out;
2555 2556 case -NFS4ERR_WRONGSEC:
2556 2557 err = -EPERM;
... ... @@ -2591,6 +2592,21 @@
2591 2592 return status;
2592 2593 }
2593 2594  
  2595 +struct rpc_clnt *
  2596 +nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
  2597 + struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  2598 +{
  2599 + int status;
  2600 + struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
  2601 +
  2602 + status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
  2603 + if (status < 0) {
  2604 + rpc_shutdown_client(client);
  2605 + return ERR_PTR(status);
  2606 + }
  2607 + return client;
  2608 +}
  2609 +
2594 2610 static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
2595 2611 {
2596 2612 struct nfs_server *server = NFS_SERVER(inode);
... ... @@ -4951,8 +4967,10 @@
4951 4967 fattr->nlink = 2;
4952 4968 }
4953 4969  
4954   -static int _nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
4955   - struct nfs4_fs_locations *fs_locations, struct page *page)
  4970 +static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
  4971 + const struct qstr *name,
  4972 + struct nfs4_fs_locations *fs_locations,
  4973 + struct page *page)
4956 4974 {
4957 4975 struct nfs_server *server = NFS_SERVER(dir);
4958 4976 u32 bitmask[2] = {
4959 4977  
4960 4978  
... ... @@ -4986,19 +5004,21 @@
4986 5004 nfs_fattr_init(&fs_locations->fattr);
4987 5005 fs_locations->server = server;
4988 5006 fs_locations->nlocations = 0;
4989   - status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  5007 + status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
4990 5008 dprintk("%s: returned status = %d\n", __func__, status);
4991 5009 return status;
4992 5010 }
4993 5011  
4994   -int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
4995   - struct nfs4_fs_locations *fs_locations, struct page *page)
  5012 +int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
  5013 + const struct qstr *name,
  5014 + struct nfs4_fs_locations *fs_locations,
  5015 + struct page *page)
4996 5016 {
4997 5017 struct nfs4_exception exception = { };
4998 5018 int err;
4999 5019 do {
5000 5020 err = nfs4_handle_exception(NFS_SERVER(dir),
5001   - _nfs4_proc_fs_locations(dir, name, fs_locations, page),
  5021 + _nfs4_proc_fs_locations(client, dir, name, fs_locations, page),
5002 5022 &exception);
5003 5023 } while (exception.retry);
5004 5024 return err;