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/config.h>
12 #include <linux/dcache.h>
13 #include <linux/mount.h>
14 #include <linux/namei.h>
15 #include <linux/nfs_fs.h>
16 #include <linux/string.h>
17 #include <linux/sunrpc/clnt.h>
18 #include <linux/vfs.h>
19 #include <linux/inet.h>
22 #define NFSDBG_FACILITY NFSDBG_VFS
25 * Check if fs_root is valid
27 static inline char *nfs4_pathname_string(const struct nfs4_pathname
*pathname
,
28 char *buffer
, ssize_t buflen
)
30 char *end
= buffer
+ buflen
;
36 n
= pathname
->ncomponents
;
38 const struct nfs4_string
*component
= &pathname
->components
[n
];
39 buflen
-= component
->len
+ 1;
42 end
-= component
->len
;
43 memcpy(end
, component
->data
, component
->len
);
48 return ERR_PTR(-ENAMETOOLONG
);
52 * Determine the mount path as a string
54 static char *nfs4_path(const struct vfsmount
*mnt_parent
,
55 const struct dentry
*dentry
,
56 char *buffer
, ssize_t buflen
)
60 srvpath
= strchr(mnt_parent
->mnt_devname
, ':');
64 srvpath
= mnt_parent
->mnt_devname
;
66 return nfs_path(srvpath
, mnt_parent
->mnt_root
, dentry
, buffer
, buflen
);
70 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
71 * believe to be the server path to this dentry
73 static int nfs4_validate_fspath(const struct vfsmount
*mnt_parent
,
74 const struct dentry
*dentry
,
75 const struct nfs4_fs_locations
*locations
,
76 char *page
, char *page2
)
78 const char *path
, *fs_path
;
80 path
= nfs4_path(mnt_parent
, dentry
, page
, PAGE_SIZE
);
84 fs_path
= nfs4_pathname_string(&locations
->fs_path
, page2
, PAGE_SIZE
);
86 return PTR_ERR(fs_path
);
88 if (strncmp(path
, fs_path
, strlen(fs_path
)) != 0) {
89 dprintk("%s: path %s does not begin with fsroot %s\n",
90 __FUNCTION__
, path
, fs_path
);
98 * Check if the string represents a "valid" IPv4 address
100 static inline int valid_ipaddr4(const char *buf
)
102 int rc
, count
, in
[4];
104 rc
= sscanf(buf
, "%d.%d.%d.%d", &in
[0], &in
[1], &in
[2], &in
[3]);
107 for (count
= 0; count
< 4; count
++) {
115 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
116 * @mnt_parent - mountpoint of parent directory
117 * @dentry - parent directory
118 * @fspath - fs path returned in fs_locations
119 * @mntpath - mount path to new server
120 * @hostname - hostname of new server
121 * @addr - host addr of new server
124 static struct vfsmount
*nfs_follow_referral(const struct vfsmount
*mnt_parent
,
125 const struct dentry
*dentry
,
126 const struct nfs4_fs_locations
*locations
)
128 struct vfsmount
*mnt
= ERR_PTR(-ENOENT
);
129 struct nfs_clone_mount mountdata
= {
130 .sb
= mnt_parent
->mnt_sb
,
132 .authflavor
= NFS_SB(mnt_parent
->mnt_sb
)->client
->cl_auth
->au_flavor
,
134 char *page
= NULL
, *page2
= NULL
;
138 if (locations
== NULL
|| locations
->nlocations
<= 0)
141 dprintk("%s: referral at %s/%s\n", __FUNCTION__
,
142 dentry
->d_parent
->d_name
.name
, dentry
->d_name
.name
);
144 page
= (char *) __get_free_page(GFP_USER
);
148 page2
= (char *) __get_free_page(GFP_USER
);
152 /* Ensure fs path is a prefix of current dentry path */
153 error
= nfs4_validate_fspath(mnt_parent
, dentry
, locations
, page
, page2
);
155 mnt
= ERR_PTR(error
);
159 devname
= nfs_devname(mnt_parent
, dentry
, page
, PAGE_SIZE
);
160 if (IS_ERR(devname
)) {
161 mnt
= (struct vfsmount
*)devname
;
166 while (loc
< locations
->nlocations
&& IS_ERR(mnt
)) {
167 const struct nfs4_fs_location
*location
= &locations
->locations
[loc
];
170 if (location
== NULL
|| location
->nservers
<= 0 ||
171 location
->rootpath
.ncomponents
== 0) {
176 mnt_path
= nfs4_pathname_string(&location
->rootpath
, page2
, PAGE_SIZE
);
177 if (IS_ERR(mnt_path
)) {
181 mountdata
.mnt_path
= mnt_path
;
184 while (s
< location
->nservers
) {
185 struct sockaddr_in addr
= {};
187 if (location
->servers
[s
].len
<= 0 ||
188 valid_ipaddr4(location
->servers
[s
].data
) < 0) {
193 mountdata
.hostname
= location
->servers
[s
].data
;
194 addr
.sin_addr
.s_addr
= in_aton(mountdata
.hostname
);
195 addr
.sin_family
= AF_INET
;
196 addr
.sin_port
= htons(NFS_PORT
);
197 mountdata
.addr
= &addr
;
199 mnt
= vfs_kern_mount(&nfs4_referral_fs_type
, 0, devname
, &mountdata
);
209 free_page((unsigned long) page
);
210 free_page((unsigned long) page2
);
211 dprintk("%s: done\n", __FUNCTION__
);
216 * nfs_do_refmount - handle crossing a referral on server
217 * @dentry - dentry of referral
218 * @nd - nameidata info
221 struct vfsmount
*nfs_do_refmount(const struct vfsmount
*mnt_parent
, struct dentry
*dentry
)
223 struct vfsmount
*mnt
= ERR_PTR(-ENOMEM
);
224 struct dentry
*parent
;
225 struct nfs4_fs_locations
*fs_locations
= NULL
;
229 /* BUG_ON(IS_ROOT(dentry)); */
230 dprintk("%s: enter\n", __FUNCTION__
);
232 page
= alloc_page(GFP_KERNEL
);
236 fs_locations
= kmalloc(sizeof(struct nfs4_fs_locations
), GFP_KERNEL
);
237 if (fs_locations
== NULL
)
241 mnt
= ERR_PTR(-ENOENT
);
243 parent
= dget_parent(dentry
);
244 dprintk("%s: getting locations for %s/%s\n",
245 __FUNCTION__
, parent
->d_name
.name
, dentry
->d_name
.name
);
247 err
= nfs4_proc_fs_locations(parent
->d_inode
, dentry
, fs_locations
, page
);
250 fs_locations
->nlocations
<= 0 ||
251 fs_locations
->fs_path
.ncomponents
<= 0)
254 mnt
= nfs_follow_referral(mnt_parent
, dentry
, fs_locations
);
259 dprintk("%s: done\n", __FUNCTION__
);