diff options
| author | Bryan Schumaker <bjschuma@netapp.com> | 2012-04-27 13:27:41 -0400 |
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-04-27 14:10:02 -0400 |
| commit | f05d147f7e3cf0d86b3a4bd5603029a7cb109633 (patch) | |
| tree | e351ef7dd3d541626f5cccef86f50906bfb3a649 | |
| parent | 72de53ec4bca39c26709122a8f78bfefe7b6bca4 (diff) | |
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>
| -rw-r--r-- | fs/nfs/internal.h | 4 | ||||
| -rw-r--r-- | fs/nfs/namespace.c | 44 | ||||
| -rw-r--r-- | fs/nfs/nfs4_fs.h | 6 | ||||
| -rw-r--r-- | fs/nfs/nfs4namespace.c | 4 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 40 |
5 files changed, 72 insertions, 26 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 45966d953169..49c09b4a535a 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
| @@ -186,10 +186,10 @@ static inline void nfs_fs_proc_exit(void) | |||
| 186 | 186 | ||
| 187 | /* nfs4namespace.c */ | 187 | /* nfs4namespace.c */ |
| 188 | #ifdef CONFIG_NFS_V4 | 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 | #else | 190 | #else |
| 191 | static inline | 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 | return ERR_PTR(-ENOENT); | 194 | return ERR_PTR(-ENOENT); |
| 195 | } | 195 | } |
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 1807866bb3ab..b9a593d056b3 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
| @@ -200,6 +200,22 @@ out_shutdown: | |||
| 200 | out: | 200 | out: |
| 201 | return err; | 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 | #else /* CONFIG_NFS_V4 */ | 219 | #else /* CONFIG_NFS_V4 */ |
| 204 | static inline int nfs_lookup_with_sec(struct nfs_server *server, | 220 | static inline int nfs_lookup_with_sec(struct nfs_server *server, |
| 205 | struct dentry *parent, struct dentry *dentry, | 221 | struct dentry *parent, struct dentry *dentry, |
| @@ -209,6 +225,17 @@ static inline int nfs_lookup_with_sec(struct nfs_server *server, | |||
| 209 | { | 225 | { |
| 210 | return -EPERM; | 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 | #endif /* CONFIG_NFS_V4 */ | 239 | #endif /* CONFIG_NFS_V4 */ |
| 213 | 240 | ||
| 214 | /* | 241 | /* |
| @@ -226,11 +253,10 @@ static inline int nfs_lookup_with_sec(struct nfs_server *server, | |||
| 226 | struct vfsmount *nfs_d_automount(struct path *path) | 253 | struct vfsmount *nfs_d_automount(struct path *path) |
| 227 | { | 254 | { |
| 228 | struct vfsmount *mnt; | 255 | struct vfsmount *mnt; |
| 229 | struct nfs_server *server = NFS_SERVER(path->dentry->d_inode); | ||
| 230 | struct dentry *parent; | 256 | struct dentry *parent; |
| 231 | struct nfs_fh *fh = NULL; | 257 | struct nfs_fh *fh = NULL; |
| 232 | struct nfs_fattr *fattr = NULL; | 258 | struct nfs_fattr *fattr = NULL; |
| 233 | int err; | 259 | struct rpc_clnt *client; |
| 234 | rpc_authflavor_t flavor = RPC_AUTH_UNIX; | 260 | rpc_authflavor_t flavor = RPC_AUTH_UNIX; |
| 235 | 261 | ||
| 236 | dprintk("--> nfs_d_automount()\n"); | 262 | dprintk("--> nfs_d_automount()\n"); |
| @@ -249,21 +275,19 @@ struct vfsmount *nfs_d_automount(struct path *path) | |||
| 249 | 275 | ||
| 250 | /* Look it up again to get its attributes */ | 276 | /* Look it up again to get its attributes */ |
| 251 | parent = dget_parent(path->dentry); | 277 | parent = dget_parent(path->dentry); |
| 252 | err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode, | 278 | client = nfs_lookup_mountpoint(parent->d_inode, &path->dentry->d_name, fh, fattr); |
| 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); | ||
| 257 | dput(parent); | 279 | dput(parent); |
| 258 | if (err != 0) { | 280 | if (IS_ERR(client)) { |
| 259 | mnt = ERR_PTR(err); | 281 | mnt = ERR_CAST(client); |
| 260 | goto out; | 282 | goto out; |
| 261 | } | 283 | } |
| 262 | 284 | ||
| 263 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) | 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 | else | 287 | else |
| 266 | mnt = nfs_do_submount(path->dentry, fh, fattr, flavor); | 288 | mnt = nfs_do_submount(path->dentry, fh, fattr, flavor); |
| 289 | rpc_shutdown_client(client); | ||
| 290 | |||
| 267 | if (IS_ERR(mnt)) | 291 | if (IS_ERR(mnt)) |
| 268 | goto out; | 292 | goto out; |
| 269 | 293 | ||
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index c82c2cda3dfd..8d75021020b3 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
| @@ -216,8 +216,10 @@ extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); | |||
| 216 | extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); | 216 | extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); |
| 217 | extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc); | 217 | extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc); |
| 218 | extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); | 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, | 219 | extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *, |
| 220 | struct nfs4_fs_locations *fs_locations, struct page *page); | 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 | extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); | 223 | extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); |
| 222 | extern int nfs4_release_lockowner(struct nfs4_lock_state *); | 224 | extern int nfs4_release_lockowner(struct nfs4_lock_state *); |
| 223 | extern const struct xattr_handler *nfs4_xattr_handlers[]; | 225 | extern const struct xattr_handler *nfs4_xattr_handlers[]; |
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 9f8681bf90de..a7f3dedc4ec7 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c | |||
| @@ -300,7 +300,7 @@ out: | |||
| 300 | * @dentry - dentry of referral | 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 | struct vfsmount *mnt = ERR_PTR(-ENOMEM); | 305 | struct vfsmount *mnt = ERR_PTR(-ENOMEM); |
| 306 | struct dentry *parent; | 306 | struct dentry *parent; |
| @@ -326,7 +326,7 @@ struct vfsmount *nfs_do_refmount(struct dentry *dentry) | |||
| 326 | dprintk("%s: getting locations for %s/%s\n", | 326 | dprintk("%s: getting locations for %s/%s\n", |
| 327 | __func__, parent->d_name.name, dentry->d_name.name); | 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 | dput(parent); | 330 | dput(parent); |
| 331 | if (err != 0 || | 331 | if (err != 0 || |
| 332 | fs_locations->nlocations <= 0 || | 332 | fs_locations->nlocations <= 0 || |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 3d92fe6be780..75eb883ed4ce 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -2377,8 +2377,9 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, | |||
| 2377 | * Note that we'll actually follow the referral later when | 2377 | * Note that we'll actually follow the referral later when |
| 2378 | * we detect fsid mismatch in inode revalidation | 2378 | * we detect fsid mismatch in inode revalidation |
| 2379 | */ | 2379 | */ |
| 2380 | static int nfs4_get_referral(struct inode *dir, const struct qstr *name, | 2380 | static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir, |
| 2381 | struct nfs_fattr *fattr, struct nfs_fh *fhandle) | 2381 | const struct qstr *name, struct nfs_fattr *fattr, |
| 2382 | struct nfs_fh *fhandle) | ||
| 2382 | { | 2383 | { |
| 2383 | int status = -ENOMEM; | 2384 | int status = -ENOMEM; |
| 2384 | struct page *page = NULL; | 2385 | struct page *page = NULL; |
| @@ -2391,7 +2392,7 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, | |||
| 2391 | if (locations == NULL) | 2392 | if (locations == NULL) |
| 2392 | goto out; | 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 | if (status != 0) | 2396 | if (status != 0) |
| 2396 | goto out; | 2397 | goto out; |
| 2397 | /* Make sure server returned a different fsid for the referral */ | 2398 | /* Make sure server returned a different fsid for the referral */ |
| @@ -2550,7 +2551,7 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, | |||
| 2550 | err = -ENOENT; | 2551 | err = -ENOENT; |
| 2551 | goto out; | 2552 | goto out; |
| 2552 | case -NFS4ERR_MOVED: | 2553 | case -NFS4ERR_MOVED: |
| 2553 | err = nfs4_get_referral(dir, name, fattr, fhandle); | 2554 | err = nfs4_get_referral(client, dir, name, fattr, fhandle); |
| 2554 | goto out; | 2555 | goto out; |
| 2555 | case -NFS4ERR_WRONGSEC: | 2556 | case -NFS4ERR_WRONGSEC: |
| 2556 | err = -EPERM; | 2557 | err = -EPERM; |
| @@ -2591,6 +2592,21 @@ static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qst | |||
| 2591 | return status; | 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 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) | 2610 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) |
| 2595 | { | 2611 | { |
| 2596 | struct nfs_server *server = NFS_SERVER(inode); | 2612 | struct nfs_server *server = NFS_SERVER(inode); |
| @@ -4951,8 +4967,10 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr) | |||
| 4951 | fattr->nlink = 2; | 4967 | fattr->nlink = 2; |
| 4952 | } | 4968 | } |
| 4953 | 4969 | ||
| 4954 | static int _nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, | 4970 | static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, |
| 4955 | struct nfs4_fs_locations *fs_locations, struct page *page) | 4971 | const struct qstr *name, |
| 4972 | struct nfs4_fs_locations *fs_locations, | ||
| 4973 | struct page *page) | ||
| 4956 | { | 4974 | { |
| 4957 | struct nfs_server *server = NFS_SERVER(dir); | 4975 | struct nfs_server *server = NFS_SERVER(dir); |
| 4958 | u32 bitmask[2] = { | 4976 | u32 bitmask[2] = { |
| @@ -4986,19 +5004,21 @@ static int _nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, | |||
| 4986 | nfs_fattr_init(&fs_locations->fattr); | 5004 | nfs_fattr_init(&fs_locations->fattr); |
| 4987 | fs_locations->server = server; | 5005 | fs_locations->server = server; |
| 4988 | fs_locations->nlocations = 0; | 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 | dprintk("%s: returned status = %d\n", __func__, status); | 5008 | dprintk("%s: returned status = %d\n", __func__, status); |
| 4991 | return status; | 5009 | return status; |
| 4992 | } | 5010 | } |
| 4993 | 5011 | ||
| 4994 | int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, | 5012 | int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, |
| 4995 | struct nfs4_fs_locations *fs_locations, struct page *page) | 5013 | const struct qstr *name, |
| 5014 | struct nfs4_fs_locations *fs_locations, | ||
| 5015 | struct page *page) | ||
| 4996 | { | 5016 | { |
| 4997 | struct nfs4_exception exception = { }; | 5017 | struct nfs4_exception exception = { }; |
| 4998 | int err; | 5018 | int err; |
| 4999 | do { | 5019 | do { |
| 5000 | err = nfs4_handle_exception(NFS_SERVER(dir), | 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 | &exception); | 5022 | &exception); |
| 5003 | } while (exception.retry); | 5023 | } while (exception.retry); |
| 5004 | return err; | 5024 | return err; |
