[PATCH] knfsd: lockd: Add nlm_destroy_host
[linux-2.6] / fs / nfs / nfs4namespace.c
1 /*
2  * linux/fs/nfs/nfs4namespace.c
3  *
4  * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
5  * - Modified by David Howells <dhowells@redhat.com>
6  *
7  * NFSv4 namespace
8  */
9
10 #include <linux/config.h>
11
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>
20 #include "internal.h"
21
22 #define NFSDBG_FACILITY         NFSDBG_VFS
23
24 /*
25  * Check if fs_root is valid
26  */
27 static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
28                                          char *buffer, ssize_t buflen)
29 {
30         char *end = buffer + buflen;
31         int n;
32
33         *--end = '\0';
34         buflen--;
35
36         n = pathname->ncomponents;
37         while (--n >= 0) {
38                 const struct nfs4_string *component = &pathname->components[n];
39                 buflen -= component->len + 1;
40                 if (buflen < 0)
41                         goto Elong;
42                 end -= component->len;
43                 memcpy(end, component->data, component->len);
44                 *--end = '/';
45         }
46         return end;
47 Elong:
48         return ERR_PTR(-ENAMETOOLONG);
49 }
50
51 /*
52  * Determine the mount path as a string
53  */
54 static char *nfs4_path(const struct vfsmount *mnt_parent,
55                        const struct dentry *dentry,
56                        char *buffer, ssize_t buflen)
57 {
58         const char *srvpath;
59
60         srvpath = strchr(mnt_parent->mnt_devname, ':');
61         if (srvpath)
62                 srvpath++;
63         else
64                 srvpath = mnt_parent->mnt_devname;
65
66         return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen);
67 }
68
69 /*
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
72  */
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)
77 {
78         const char *path, *fs_path;
79
80         path = nfs4_path(mnt_parent, dentry, page, PAGE_SIZE);
81         if (IS_ERR(path))
82                 return PTR_ERR(path);
83
84         fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
85         if (IS_ERR(fs_path))
86                 return PTR_ERR(fs_path);
87
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);
91                 return -ENOENT;
92         }
93
94         return 0;
95 }
96
97 /*
98  * Check if the string represents a "valid" IPv4 address
99  */
100 static inline int valid_ipaddr4(const char *buf)
101 {
102         int rc, count, in[4];
103
104         rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
105         if (rc != 4)
106                 return -EINVAL;
107         for (count = 0; count < 4; count++) {
108                 if (in[count] > 255)
109                         return -EINVAL;
110         }
111         return 0;
112 }
113
114 /**
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
122  *
123  */
124 static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
125                                             const struct dentry *dentry,
126                                             const struct nfs4_fs_locations *locations)
127 {
128         struct vfsmount *mnt = ERR_PTR(-ENOENT);
129         struct nfs_clone_mount mountdata = {
130                 .sb = mnt_parent->mnt_sb,
131                 .dentry = dentry,
132                 .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor,
133         };
134         char *page = NULL, *page2 = NULL;
135         char *devname;
136         int loc, s, error;
137
138         if (locations == NULL || locations->nlocations <= 0)
139                 goto out;
140
141         dprintk("%s: referral at %s/%s\n", __FUNCTION__,
142                 dentry->d_parent->d_name.name, dentry->d_name.name);
143
144         page = (char *) __get_free_page(GFP_USER);
145         if (!page)
146                 goto out;
147
148         page2 = (char *) __get_free_page(GFP_USER);
149         if (!page2)
150                 goto out;
151
152         /* Ensure fs path is a prefix of current dentry path */
153         error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2);
154         if (error < 0) {
155                 mnt = ERR_PTR(error);
156                 goto out;
157         }
158
159         devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE);
160         if (IS_ERR(devname)) {
161                 mnt = (struct vfsmount *)devname;
162                 goto out;
163         }
164
165         loc = 0;
166         while (loc < locations->nlocations && IS_ERR(mnt)) {
167                 const struct nfs4_fs_location *location = &locations->locations[loc];
168                 char *mnt_path;
169
170                 if (location == NULL || location->nservers <= 0 ||
171                     location->rootpath.ncomponents == 0) {
172                         loc++;
173                         continue;
174                 }
175
176                 mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
177                 if (IS_ERR(mnt_path)) {
178                         loc++;
179                         continue;
180                 }
181                 mountdata.mnt_path = mnt_path;
182
183                 s = 0;
184                 while (s < location->nservers) {
185                         struct sockaddr_in addr = {};
186
187                         if (location->servers[s].len <= 0 ||
188                             valid_ipaddr4(location->servers[s].data) < 0) {
189                                 s++;
190                                 continue;
191                         }
192
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;
198
199                         mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, devname, &mountdata);
200                         if (!IS_ERR(mnt)) {
201                                 break;
202                         }
203                         s++;
204                 }
205                 loc++;
206         }
207
208 out:
209         free_page((unsigned long) page);
210         free_page((unsigned long) page2);
211         dprintk("%s: done\n", __FUNCTION__);
212         return mnt;
213 }
214
215 /*
216  * nfs_do_refmount - handle crossing a referral on server
217  * @dentry - dentry of referral
218  * @nd - nameidata info
219  *
220  */
221 struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
222 {
223         struct vfsmount *mnt = ERR_PTR(-ENOMEM);
224         struct dentry *parent;
225         struct nfs4_fs_locations *fs_locations = NULL;
226         struct page *page;
227         int err;
228
229         /* BUG_ON(IS_ROOT(dentry)); */
230         dprintk("%s: enter\n", __FUNCTION__);
231
232         page = alloc_page(GFP_KERNEL);
233         if (page == NULL)
234                 goto out;
235
236         fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
237         if (fs_locations == NULL)
238                 goto out_free;
239
240         /* Get locations */
241         mnt = ERR_PTR(-ENOENT);
242
243         parent = dget_parent(dentry);
244         dprintk("%s: getting locations for %s/%s\n",
245                 __FUNCTION__, parent->d_name.name, dentry->d_name.name);
246
247         err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page);
248         dput(parent);
249         if (err != 0 ||
250             fs_locations->nlocations <= 0 ||
251             fs_locations->fs_path.ncomponents <= 0)
252                 goto out_free;
253
254         mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations);
255 out_free:
256         __free_page(page);
257         kfree(fs_locations);
258 out:
259         dprintk("%s: done\n", __FUNCTION__);
260         return mnt;
261 }