2 * linux/fs/nfs/nfs4namespace.c
4 * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
5 * - Modified by David Howells <dhowells@redhat.com>
10 #include <linux/dcache.h>
11 #include <linux/mount.h>
12 #include <linux/namei.h>
13 #include <linux/nfs_fs.h>
14 #include <linux/slab.h>
15 #include <linux/string.h>
16 #include <linux/sunrpc/clnt.h>
17 #include <linux/sunrpc/addr.h>
18 #include <linux/vfs.h>
19 #include <linux/inet.h>
22 #include "dns_resolve.h"
24 #define NFSDBG_FACILITY NFSDBG_VFS
27 * Convert the NFSv4 pathname components into a standard posix path.
29 * Note that the resulting string will be placed at the end of the buffer
31 static inline char *nfs4_pathname_string(const struct nfs4_pathname
*pathname
,
32 char *buffer
, ssize_t buflen
)
34 char *end
= buffer
+ buflen
;
40 n
= pathname
->ncomponents
;
42 const struct nfs4_string
*component
= &pathname
->components
[n
];
43 buflen
-= component
->len
+ 1;
46 end
-= component
->len
;
47 memcpy(end
, component
->data
, component
->len
);
52 return ERR_PTR(-ENAMETOOLONG
);
56 * return the path component of "<server>:<path>"
57 * nfspath - the "<server>:<path>" string
58 * end - one past the last char that could contain "<server>:"
59 * returns NULL on failure
61 static char *nfs_path_component(const char *nfspath
, const char *end
)
65 if (*nfspath
== '[') {
66 /* parse [] escaped IPv6 addrs */
67 p
= strchr(nfspath
, ']');
68 if (p
!= NULL
&& ++p
< end
&& *p
== ':')
71 /* otherwise split on first colon */
72 p
= strchr(nfspath
, ':');
73 if (p
!= NULL
&& p
< end
)
80 * Determine the mount path as a string
82 static char *nfs4_path(struct dentry
*dentry
, char *buffer
, ssize_t buflen
)
85 char *path
= nfs_path(&limit
, dentry
, buffer
, buflen
,
88 char *path_component
= nfs_path_component(path
, limit
);
90 return path_component
;
96 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
97 * believe to be the server path to this dentry
99 static int nfs4_validate_fspath(struct dentry
*dentry
,
100 const struct nfs4_fs_locations
*locations
,
101 char *page
, char *page2
)
103 const char *path
, *fs_path
;
105 path
= nfs4_path(dentry
, page
, PAGE_SIZE
);
107 return PTR_ERR(path
);
109 fs_path
= nfs4_pathname_string(&locations
->fs_path
, page2
, PAGE_SIZE
);
111 return PTR_ERR(fs_path
);
113 if (strncmp(path
, fs_path
, strlen(fs_path
)) != 0) {
114 dprintk("%s: path %s does not begin with fsroot %s\n",
115 __func__
, path
, fs_path
);
122 static size_t nfs_parse_server_name(char *string
, size_t len
,
123 struct sockaddr
*sa
, size_t salen
, struct nfs_server
*server
)
125 struct net
*net
= rpc_net_ns(server
->client
);
128 ret
= rpc_pton(net
, string
, len
, sa
, salen
);
130 ret
= nfs_dns_resolve_name(net
, string
, len
, sa
, salen
);
137 rpc_authflavor_t
nfs_find_best_sec(struct nfs4_secinfo_flavors
*flavors
)
139 struct gss_api_mech
*mech
;
140 struct xdr_netobj oid
;
142 rpc_authflavor_t pseudoflavor
= RPC_AUTH_UNIX
;
144 for (i
= 0; i
< flavors
->num_flavors
; i
++) {
145 struct nfs4_secinfo_flavor
*flavor
;
146 flavor
= &flavors
->flavors
[i
];
148 if (flavor
->flavor
== RPC_AUTH_NULL
|| flavor
->flavor
== RPC_AUTH_UNIX
) {
149 pseudoflavor
= flavor
->flavor
;
151 } else if (flavor
->flavor
== RPC_AUTH_GSS
) {
152 oid
.len
= flavor
->gss
.sec_oid4
.len
;
153 oid
.data
= flavor
->gss
.sec_oid4
.data
;
154 mech
= gss_mech_get_by_OID(&oid
);
157 pseudoflavor
= gss_svc_to_pseudoflavor(mech
, flavor
->gss
.service
);
166 static rpc_authflavor_t
nfs4_negotiate_security(struct inode
*inode
, struct qstr
*name
)
169 struct nfs4_secinfo_flavors
*flavors
;
170 rpc_authflavor_t flavor
;
173 page
= alloc_page(GFP_KERNEL
);
176 flavors
= page_address(page
);
178 err
= nfs4_proc_secinfo(inode
, name
, flavors
);
184 flavor
= nfs_find_best_sec(flavors
);
192 * Please call rpc_shutdown_client() when you are done with this client.
194 struct rpc_clnt
*nfs4_create_sec_client(struct rpc_clnt
*clnt
, struct inode
*inode
,
197 rpc_authflavor_t flavor
;
199 flavor
= nfs4_negotiate_security(inode
, name
);
201 return ERR_PTR((int)flavor
);
203 return rpc_clone_client_set_auth(clnt
, flavor
);
206 static struct vfsmount
*try_location(struct nfs_clone_mount
*mountdata
,
207 char *page
, char *page2
,
208 const struct nfs4_fs_location
*location
)
210 const size_t addr_bufsize
= sizeof(struct sockaddr_storage
);
211 struct vfsmount
*mnt
= ERR_PTR(-ENOENT
);
213 unsigned int maxbuflen
;
216 mnt_path
= nfs4_pathname_string(&location
->rootpath
, page2
, PAGE_SIZE
);
217 if (IS_ERR(mnt_path
))
218 return ERR_CAST(mnt_path
);
219 mountdata
->mnt_path
= mnt_path
;
220 maxbuflen
= mnt_path
- 1 - page2
;
222 mountdata
->addr
= kmalloc(addr_bufsize
, GFP_KERNEL
);
223 if (mountdata
->addr
== NULL
)
224 return ERR_PTR(-ENOMEM
);
226 for (s
= 0; s
< location
->nservers
; s
++) {
227 const struct nfs4_string
*buf
= &location
->servers
[s
];
229 if (buf
->len
<= 0 || buf
->len
>= maxbuflen
)
232 if (memchr(buf
->data
, IPV6_SCOPE_DELIMITER
, buf
->len
))
235 mountdata
->addrlen
= nfs_parse_server_name(buf
->data
, buf
->len
,
236 mountdata
->addr
, addr_bufsize
,
237 NFS_SB(mountdata
->sb
));
238 if (mountdata
->addrlen
== 0)
241 rpc_set_port(mountdata
->addr
, NFS_PORT
);
243 memcpy(page2
, buf
->data
, buf
->len
);
244 page2
[buf
->len
] = '\0';
245 mountdata
->hostname
= page2
;
247 snprintf(page
, PAGE_SIZE
, "%s:%s",
249 mountdata
->mnt_path
);
251 mnt
= vfs_kern_mount(&nfs4_referral_fs_type
, 0, page
, mountdata
);
255 kfree(mountdata
->addr
);
260 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
261 * @dentry - parent directory
262 * @locations - array of NFSv4 server location information
265 static struct vfsmount
*nfs_follow_referral(struct dentry
*dentry
,
266 const struct nfs4_fs_locations
*locations
)
268 struct vfsmount
*mnt
= ERR_PTR(-ENOENT
);
269 struct nfs_clone_mount mountdata
= {
272 .authflavor
= NFS_SB(dentry
->d_sb
)->client
->cl_auth
->au_flavor
,
274 char *page
= NULL
, *page2
= NULL
;
277 if (locations
== NULL
|| locations
->nlocations
<= 0)
280 dprintk("%s: referral at %s/%s\n", __func__
,
281 dentry
->d_parent
->d_name
.name
, dentry
->d_name
.name
);
283 page
= (char *) __get_free_page(GFP_USER
);
287 page2
= (char *) __get_free_page(GFP_USER
);
291 /* Ensure fs path is a prefix of current dentry path */
292 error
= nfs4_validate_fspath(dentry
, locations
, page
, page2
);
294 mnt
= ERR_PTR(error
);
298 for (loc
= 0; loc
< locations
->nlocations
; loc
++) {
299 const struct nfs4_fs_location
*location
= &locations
->locations
[loc
];
301 if (location
== NULL
|| location
->nservers
<= 0 ||
302 location
->rootpath
.ncomponents
== 0)
305 mnt
= try_location(&mountdata
, page
, page2
, location
);
311 free_page((unsigned long) page
);
312 free_page((unsigned long) page2
);
313 dprintk("%s: done\n", __func__
);
318 * nfs_do_refmount - handle crossing a referral on server
319 * @dentry - dentry of referral
322 static struct vfsmount
*nfs_do_refmount(struct rpc_clnt
*client
, struct dentry
*dentry
)
324 struct vfsmount
*mnt
= ERR_PTR(-ENOMEM
);
325 struct dentry
*parent
;
326 struct nfs4_fs_locations
*fs_locations
= NULL
;
330 /* BUG_ON(IS_ROOT(dentry)); */
331 dprintk("%s: enter\n", __func__
);
333 page
= alloc_page(GFP_KERNEL
);
337 fs_locations
= kmalloc(sizeof(struct nfs4_fs_locations
), GFP_KERNEL
);
338 if (fs_locations
== NULL
)
342 mnt
= ERR_PTR(-ENOENT
);
344 parent
= dget_parent(dentry
);
345 dprintk("%s: getting locations for %s/%s\n",
346 __func__
, parent
->d_name
.name
, dentry
->d_name
.name
);
348 err
= nfs4_proc_fs_locations(client
, parent
->d_inode
, &dentry
->d_name
, fs_locations
, page
);
351 fs_locations
->nlocations
<= 0 ||
352 fs_locations
->fs_path
.ncomponents
<= 0)
355 mnt
= nfs_follow_referral(dentry
, fs_locations
);
360 dprintk("%s: done\n", __func__
);
364 struct vfsmount
*nfs4_submount(struct nfs_server
*server
, struct dentry
*dentry
,
365 struct nfs_fh
*fh
, struct nfs_fattr
*fattr
)
367 struct dentry
*parent
= dget_parent(dentry
);
368 struct rpc_clnt
*client
;
369 struct vfsmount
*mnt
;
371 /* Look it up again to get its attributes and sec flavor */
372 client
= nfs4_proc_lookup_mountpoint(parent
->d_inode
, &dentry
->d_name
, fh
, fattr
);
375 return ERR_CAST(client
);
377 if (fattr
->valid
& NFS_ATTR_FATTR_V4_REFERRAL
)
378 mnt
= nfs_do_refmount(client
, dentry
);
380 mnt
= nfs_do_submount(dentry
, fh
, fattr
, client
->cl_auth
->au_flavor
);
382 rpc_shutdown_client(client
);