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/vfs.h>
18 #include <linux/inet.h>
21 #include "dns_resolve.h"
23 #define NFSDBG_FACILITY NFSDBG_VFS
26 * Convert the NFSv4 pathname components into a standard posix path.
28 * Note that the resulting string will be placed at the end of the buffer
30 static inline char *nfs4_pathname_string(const struct nfs4_pathname
*pathname
,
31 char *buffer
, ssize_t buflen
)
33 char *end
= buffer
+ buflen
;
39 n
= pathname
->ncomponents
;
41 const struct nfs4_string
*component
= &pathname
->components
[n
];
42 buflen
-= component
->len
+ 1;
45 end
-= component
->len
;
46 memcpy(end
, component
->data
, component
->len
);
51 return ERR_PTR(-ENAMETOOLONG
);
55 * Determine the mount path as a string
57 static char *nfs4_path(const struct vfsmount
*mnt_parent
,
58 const struct dentry
*dentry
,
59 char *buffer
, ssize_t buflen
)
63 srvpath
= strchr(mnt_parent
->mnt_devname
, ':');
67 srvpath
= mnt_parent
->mnt_devname
;
69 return nfs_path(srvpath
, mnt_parent
->mnt_root
, dentry
, buffer
, buflen
);
73 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
74 * believe to be the server path to this dentry
76 static int nfs4_validate_fspath(const struct vfsmount
*mnt_parent
,
77 const struct dentry
*dentry
,
78 const struct nfs4_fs_locations
*locations
,
79 char *page
, char *page2
)
81 const char *path
, *fs_path
;
83 path
= nfs4_path(mnt_parent
, dentry
, page
, PAGE_SIZE
);
87 fs_path
= nfs4_pathname_string(&locations
->fs_path
, page2
, PAGE_SIZE
);
89 return PTR_ERR(fs_path
);
91 if (strncmp(path
, fs_path
, strlen(fs_path
)) != 0) {
92 dprintk("%s: path %s does not begin with fsroot %s\n",
93 __func__
, path
, fs_path
);
100 static size_t nfs_parse_server_name(char *string
, size_t len
,
101 struct sockaddr
*sa
, size_t salen
)
105 ret
= rpc_pton(string
, len
, sa
, salen
);
107 ret
= nfs_dns_resolve_name(string
, len
, sa
, salen
);
114 static struct vfsmount
*try_location(struct nfs_clone_mount
*mountdata
,
115 char *page
, char *page2
,
116 const struct nfs4_fs_location
*location
)
118 const size_t addr_bufsize
= sizeof(struct sockaddr_storage
);
119 struct vfsmount
*mnt
= ERR_PTR(-ENOENT
);
121 unsigned int maxbuflen
;
124 mnt_path
= nfs4_pathname_string(&location
->rootpath
, page2
, PAGE_SIZE
);
125 if (IS_ERR(mnt_path
))
126 return ERR_CAST(mnt_path
);
127 mountdata
->mnt_path
= mnt_path
;
128 maxbuflen
= mnt_path
- 1 - page2
;
130 mountdata
->addr
= kmalloc(addr_bufsize
, GFP_KERNEL
);
131 if (mountdata
->addr
== NULL
)
132 return ERR_PTR(-ENOMEM
);
134 for (s
= 0; s
< location
->nservers
; s
++) {
135 const struct nfs4_string
*buf
= &location
->servers
[s
];
137 if (buf
->len
<= 0 || buf
->len
>= maxbuflen
)
140 if (memchr(buf
->data
, IPV6_SCOPE_DELIMITER
, buf
->len
))
143 mountdata
->addrlen
= nfs_parse_server_name(buf
->data
, buf
->len
,
144 mountdata
->addr
, addr_bufsize
);
145 if (mountdata
->addrlen
== 0)
148 rpc_set_port(mountdata
->addr
, NFS_PORT
);
150 memcpy(page2
, buf
->data
, buf
->len
);
151 page2
[buf
->len
] = '\0';
152 mountdata
->hostname
= page2
;
154 snprintf(page
, PAGE_SIZE
, "%s:%s",
156 mountdata
->mnt_path
);
158 mnt
= vfs_kern_mount(&nfs4_referral_fs_type
, 0, page
, mountdata
);
162 kfree(mountdata
->addr
);
167 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
168 * @mnt_parent - mountpoint of parent directory
169 * @dentry - parent directory
170 * @locations - array of NFSv4 server location information
173 static struct vfsmount
*nfs_follow_referral(const struct vfsmount
*mnt_parent
,
174 const struct dentry
*dentry
,
175 const struct nfs4_fs_locations
*locations
)
177 struct vfsmount
*mnt
= ERR_PTR(-ENOENT
);
178 struct nfs_clone_mount mountdata
= {
179 .sb
= mnt_parent
->mnt_sb
,
181 .authflavor
= NFS_SB(mnt_parent
->mnt_sb
)->client
->cl_auth
->au_flavor
,
183 char *page
= NULL
, *page2
= NULL
;
186 if (locations
== NULL
|| locations
->nlocations
<= 0)
189 dprintk("%s: referral at %s/%s\n", __func__
,
190 dentry
->d_parent
->d_name
.name
, dentry
->d_name
.name
);
192 page
= (char *) __get_free_page(GFP_USER
);
196 page2
= (char *) __get_free_page(GFP_USER
);
200 /* Ensure fs path is a prefix of current dentry path */
201 error
= nfs4_validate_fspath(mnt_parent
, dentry
, locations
, page
, page2
);
203 mnt
= ERR_PTR(error
);
207 for (loc
= 0; loc
< locations
->nlocations
; loc
++) {
208 const struct nfs4_fs_location
*location
= &locations
->locations
[loc
];
210 if (location
== NULL
|| location
->nservers
<= 0 ||
211 location
->rootpath
.ncomponents
== 0)
214 mnt
= try_location(&mountdata
, page
, page2
, location
);
220 free_page((unsigned long) page
);
221 free_page((unsigned long) page2
);
222 dprintk("%s: done\n", __func__
);
227 * nfs_do_refmount - handle crossing a referral on server
228 * @mnt_parent - mountpoint of referral
229 * @dentry - dentry of referral
232 struct vfsmount
*nfs_do_refmount(const struct vfsmount
*mnt_parent
, struct dentry
*dentry
)
234 struct vfsmount
*mnt
= ERR_PTR(-ENOMEM
);
235 struct dentry
*parent
;
236 struct nfs4_fs_locations
*fs_locations
= NULL
;
240 /* BUG_ON(IS_ROOT(dentry)); */
241 dprintk("%s: enter\n", __func__
);
243 page
= alloc_page(GFP_KERNEL
);
247 fs_locations
= kmalloc(sizeof(struct nfs4_fs_locations
), GFP_KERNEL
);
248 if (fs_locations
== NULL
)
252 mnt
= ERR_PTR(-ENOENT
);
254 parent
= dget_parent(dentry
);
255 dprintk("%s: getting locations for %s/%s\n",
256 __func__
, parent
->d_name
.name
, dentry
->d_name
.name
);
258 err
= nfs4_proc_fs_locations(parent
->d_inode
, &dentry
->d_name
, fs_locations
, page
);
261 fs_locations
->nlocations
<= 0 ||
262 fs_locations
->fs_path
.ncomponents
<= 0)
265 mnt
= nfs_follow_referral(mnt_parent
, dentry
, fs_locations
);
270 dprintk("%s: done\n", __func__
);