diff options
Diffstat (limited to 'fs/nfs/nfs4namespace.c')
-rw-r--r-- | fs/nfs/nfs4namespace.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c new file mode 100644 index 000000000000..ea38d27b74e6 --- /dev/null +++ b/fs/nfs/nfs4namespace.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* | ||
2 | * linux/fs/nfs/nfs4namespace.c | ||
3 | * | ||
4 | * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> | ||
5 | * | ||
6 | * NFSv4 namespace | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | |||
11 | #include <linux/dcache.h> | ||
12 | #include <linux/mount.h> | ||
13 | #include <linux/namei.h> | ||
14 | #include <linux/nfs_fs.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/sunrpc/clnt.h> | ||
17 | #include <linux/vfs.h> | ||
18 | #include <linux/inet.h> | ||
19 | #include "internal.h" | ||
20 | |||
21 | #define NFSDBG_FACILITY NFSDBG_VFS | ||
22 | |||
23 | /* | ||
24 | * Check if fs_root is valid | ||
25 | */ | ||
26 | static inline char *nfs4_pathname_string(struct nfs4_pathname *pathname, | ||
27 | char *buffer, ssize_t buflen) | ||
28 | { | ||
29 | char *end = buffer + buflen; | ||
30 | int n; | ||
31 | |||
32 | *--end = '\0'; | ||
33 | buflen--; | ||
34 | |||
35 | n = pathname->ncomponents; | ||
36 | while (--n >= 0) { | ||
37 | struct nfs4_string *component = &pathname->components[n]; | ||
38 | buflen -= component->len + 1; | ||
39 | if (buflen < 0) | ||
40 | goto Elong; | ||
41 | end -= component->len; | ||
42 | memcpy(end, component->data, component->len); | ||
43 | *--end = '/'; | ||
44 | } | ||
45 | return end; | ||
46 | Elong: | ||
47 | return ERR_PTR(-ENAMETOOLONG); | ||
48 | } | ||
49 | |||
50 | |||
51 | /** | ||
52 | * nfs_follow_referral - set up mountpoint when hitting a referral on moved error | ||
53 | * @mnt_parent - mountpoint of parent directory | ||
54 | * @dentry - parent directory | ||
55 | * @fspath - fs path returned in fs_locations | ||
56 | * @mntpath - mount path to new server | ||
57 | * @hostname - hostname of new server | ||
58 | * @addr - host addr of new server | ||
59 | * | ||
60 | */ | ||
61 | static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, | ||
62 | const struct dentry *dentry, | ||
63 | struct nfs4_fs_locations *locations) | ||
64 | { | ||
65 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | ||
66 | struct nfs_clone_mount mountdata = { | ||
67 | .sb = mnt_parent->mnt_sb, | ||
68 | .dentry = dentry, | ||
69 | .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, | ||
70 | }; | ||
71 | char *page, *page2; | ||
72 | char *path, *fs_path; | ||
73 | char *devname; | ||
74 | int loc, s; | ||
75 | |||
76 | if (locations == NULL || locations->nlocations <= 0) | ||
77 | goto out; | ||
78 | |||
79 | dprintk("%s: referral at %s/%s\n", __FUNCTION__, | ||
80 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
81 | |||
82 | /* Ensure fs path is a prefix of current dentry path */ | ||
83 | page = (char *) __get_free_page(GFP_USER); | ||
84 | if (page == NULL) | ||
85 | goto out; | ||
86 | page2 = (char *) __get_free_page(GFP_USER); | ||
87 | if (page2 == NULL) | ||
88 | goto out; | ||
89 | |||
90 | path = nfs4_path(dentry, page, PAGE_SIZE); | ||
91 | if (IS_ERR(path)) | ||
92 | goto out_free; | ||
93 | |||
94 | fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); | ||
95 | if (IS_ERR(fs_path)) | ||
96 | goto out_free; | ||
97 | |||
98 | if (strncmp(path, fs_path, strlen(fs_path)) != 0) { | ||
99 | dprintk("%s: path %s does not begin with fsroot %s\n", __FUNCTION__, path, fs_path); | ||
100 | goto out_free; | ||
101 | } | ||
102 | |||
103 | devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); | ||
104 | if (IS_ERR(devname)) { | ||
105 | mnt = (struct vfsmount *)devname; | ||
106 | goto out_free; | ||
107 | } | ||
108 | |||
109 | loc = 0; | ||
110 | while (loc < locations->nlocations && IS_ERR(mnt)) { | ||
111 | struct nfs4_fs_location *location = &locations->locations[loc]; | ||
112 | char *mnt_path; | ||
113 | |||
114 | if (location == NULL || location->nservers <= 0 || | ||
115 | location->rootpath.ncomponents == 0) { | ||
116 | loc++; | ||
117 | continue; | ||
118 | } | ||
119 | |||
120 | mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); | ||
121 | if (IS_ERR(mnt_path)) { | ||
122 | loc++; | ||
123 | continue; | ||
124 | } | ||
125 | mountdata.mnt_path = mnt_path; | ||
126 | |||
127 | s = 0; | ||
128 | while (s < location->nservers) { | ||
129 | struct sockaddr_in addr = {}; | ||
130 | |||
131 | if (location->servers[s].len <= 0 || | ||
132 | valid_ipaddr4(location->servers[s].data) < 0) { | ||
133 | s++; | ||
134 | continue; | ||
135 | } | ||
136 | |||
137 | mountdata.hostname = location->servers[s].data; | ||
138 | addr.sin_addr.s_addr = in_aton(mountdata.hostname); | ||
139 | addr.sin_family = AF_INET; | ||
140 | addr.sin_port = htons(NFS_PORT); | ||
141 | mountdata.addr = &addr; | ||
142 | |||
143 | mnt = vfs_kern_mount(&nfs_referral_nfs4_fs_type, 0, devname, &mountdata); | ||
144 | if (!IS_ERR(mnt)) { | ||
145 | break; | ||
146 | } | ||
147 | s++; | ||
148 | } | ||
149 | loc++; | ||
150 | } | ||
151 | |||
152 | out_free: | ||
153 | free_page((unsigned long)page); | ||
154 | free_page((unsigned long)page2); | ||
155 | out: | ||
156 | dprintk("%s: done\n", __FUNCTION__); | ||
157 | return mnt; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * nfs_do_refmount - handle crossing a referral on server | ||
162 | * @dentry - dentry of referral | ||
163 | * @nd - nameidata info | ||
164 | * | ||
165 | */ | ||
166 | struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) | ||
167 | { | ||
168 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | ||
169 | struct dentry *parent; | ||
170 | struct nfs4_fs_locations *fs_locations = NULL; | ||
171 | struct page *page; | ||
172 | int err; | ||
173 | |||
174 | /* BUG_ON(IS_ROOT(dentry)); */ | ||
175 | dprintk("%s: enter\n", __FUNCTION__); | ||
176 | |||
177 | page = alloc_page(GFP_KERNEL); | ||
178 | if (page == NULL) | ||
179 | goto out; | ||
180 | |||
181 | fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); | ||
182 | if (fs_locations == NULL) | ||
183 | goto out_free; | ||
184 | |||
185 | /* Get locations */ | ||
186 | parent = dget_parent(dentry); | ||
187 | dprintk("%s: getting locations for %s/%s\n", __FUNCTION__, parent->d_name.name, dentry->d_name.name); | ||
188 | err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); | ||
189 | dput(parent); | ||
190 | if (err != 0 || fs_locations->nlocations <= 0 || | ||
191 | fs_locations->fs_path.ncomponents <= 0) | ||
192 | goto out_free; | ||
193 | |||
194 | mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations); | ||
195 | out_free: | ||
196 | __free_page(page); | ||
197 | kfree(fs_locations); | ||
198 | out: | ||
199 | dprintk("%s: done\n", __FUNCTION__); | ||
200 | return mnt; | ||
201 | } | ||