1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/dcache.h>
16#include <linux/mount.h>
17#include <linux/namei.h>
18#include <linux/slab.h>
19#include <linux/vfs.h>
20#include <linux/fs.h>
21#include <linux/inet.h>
22#include "cifsglob.h"
23#include "cifsproto.h"
24#include "cifsfs.h"
25#include "dns_resolve.h"
26#include "cifs_debug.h"
27#include "cifs_unicode.h"
28
29static LIST_HEAD(cifs_dfs_automount_list);
30
31static void cifs_dfs_expire_automounts(struct work_struct *work);
32static DECLARE_DELAYED_WORK(cifs_dfs_automount_task,
33 cifs_dfs_expire_automounts);
34static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ;
35
36static void cifs_dfs_expire_automounts(struct work_struct *work)
37{
38 struct list_head *list = &cifs_dfs_automount_list;
39
40 mark_mounts_for_expiry(list);
41 if (!list_empty(list))
42 schedule_delayed_work(&cifs_dfs_automount_task,
43 cifs_dfs_mountpoint_expiry_timeout);
44}
45
46void cifs_dfs_release_automount_timer(void)
47{
48 BUG_ON(!list_empty(&cifs_dfs_automount_list));
49 cancel_delayed_work_sync(&cifs_dfs_automount_task);
50}
51
52
53
54
55
56
57
58
59
60
61
62
63
64static char *
65cifs_build_devname(char *nodename, const char *prepath)
66{
67 size_t pplen;
68 size_t unclen;
69 char *dev;
70 char *pos;
71
72
73 nodename += strspn(nodename, "\\");
74 if (!*nodename)
75 return ERR_PTR(-EINVAL);
76
77
78 unclen = strlen(nodename);
79 pos = nodename + unclen - 1;
80
81
82 while (*pos == '\\') {
83 --pos;
84 --unclen;
85 }
86
87
88
89
90
91
92 pplen = prepath ? strlen(prepath) : 0;
93 dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
94 if (!dev)
95 return ERR_PTR(-ENOMEM);
96
97 pos = dev;
98
99 *pos = '/';
100 ++pos;
101 *pos = '/';
102 ++pos;
103
104
105 memcpy(pos, nodename, unclen);
106 pos += unclen;
107
108
109 if (pplen) {
110 *pos = '/';
111 ++pos;
112 memcpy(pos, prepath, pplen);
113 pos += pplen;
114 }
115
116
117 *pos = '\0';
118
119 convert_delimiter(dev, '/');
120 return dev;
121}
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137char *cifs_compose_mount_options(const char *sb_mountdata,
138 const char *fullpath,
139 const struct dfs_info3_param *ref,
140 char **devname)
141{
142 int rc;
143 char *mountdata = NULL;
144 const char *prepath = NULL;
145 int md_len;
146 char *tkn_e;
147 char *srvIP = NULL;
148 char sep = ',';
149 int off, noff;
150
151 if (sb_mountdata == NULL)
152 return ERR_PTR(-EINVAL);
153
154 if (strlen(fullpath) - ref->path_consumed) {
155 prepath = fullpath + ref->path_consumed;
156
157 if (*prepath == '/' || *prepath == '\\')
158 prepath++;
159 }
160
161 *devname = cifs_build_devname(ref->node_name, prepath);
162 if (IS_ERR(*devname)) {
163 rc = PTR_ERR(*devname);
164 *devname = NULL;
165 goto compose_mount_options_err;
166 }
167
168 rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
169 if (rc < 0) {
170 cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
171 __func__, *devname, rc);
172 goto compose_mount_options_err;
173 }
174
175
176
177
178
179
180
181 md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
182 mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
183 if (mountdata == NULL) {
184 rc = -ENOMEM;
185 goto compose_mount_options_err;
186 }
187
188
189 off = 0;
190 if (strncmp(sb_mountdata, "sep=", 4) == 0) {
191 sep = sb_mountdata[4];
192 strncpy(mountdata, sb_mountdata, 5);
193 off += 5;
194 }
195
196 do {
197 tkn_e = strchr(sb_mountdata + off, sep);
198 if (tkn_e == NULL)
199 noff = strlen(sb_mountdata + off);
200 else
201 noff = tkn_e - (sb_mountdata + off) + 1;
202
203 if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
204 off += noff;
205 continue;
206 }
207 if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) {
208 off += noff;
209 continue;
210 }
211 if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
212 off += noff;
213 continue;
214 }
215 strncat(mountdata, sb_mountdata + off, noff);
216 off += noff;
217 } while (tkn_e);
218 strcat(mountdata, sb_mountdata + off);
219 mountdata[md_len] = '\0';
220
221
222 if (mountdata[strlen(mountdata) - 1] != sep)
223 strncat(mountdata, &sep, 1);
224 strcat(mountdata, "ip=");
225 strcat(mountdata, srvIP);
226
227
228
229
230compose_mount_options_out:
231 kfree(srvIP);
232 return mountdata;
233
234compose_mount_options_err:
235 kfree(mountdata);
236 mountdata = ERR_PTR(rc);
237 kfree(*devname);
238 *devname = NULL;
239 goto compose_mount_options_out;
240}
241
242
243
244
245
246
247
248static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,
249 const char *fullpath, const struct dfs_info3_param *ref)
250{
251 struct vfsmount *mnt;
252 char *mountdata;
253 char *devname = NULL;
254
255
256 mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
257 fullpath + 1, ref, &devname);
258
259 if (IS_ERR(mountdata))
260 return (struct vfsmount *)mountdata;
261
262 mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata);
263 kfree(mountdata);
264 kfree(devname);
265 return mnt;
266
267}
268
269static void dump_referral(const struct dfs_info3_param *ref)
270{
271 cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
272 cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
273 cifs_dbg(FYI, "DFS: fl: %hd, srv_type: %hd\n",
274 ref->flags, ref->server_type);
275 cifs_dbg(FYI, "DFS: ref_flags: %hd, path_consumed: %hd\n",
276 ref->ref_flag, ref->path_consumed);
277}
278
279
280
281
282static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
283{
284 struct dfs_info3_param *referrals = NULL;
285 unsigned int num_referrals = 0;
286 struct cifs_sb_info *cifs_sb;
287 struct cifs_ses *ses;
288 char *full_path;
289 unsigned int xid;
290 int i;
291 int rc;
292 struct vfsmount *mnt;
293 struct tcon_link *tlink;
294
295 cifs_dbg(FYI, "in %s\n", __func__);
296 BUG_ON(IS_ROOT(mntpt));
297
298
299
300
301
302
303
304 mnt = ERR_PTR(-ENOMEM);
305 full_path = build_path_from_dentry(mntpt);
306 if (full_path == NULL)
307 goto cdda_exit;
308
309 cifs_sb = CIFS_SB(mntpt->d_sb);
310 tlink = cifs_sb_tlink(cifs_sb);
311 if (IS_ERR(tlink)) {
312 mnt = ERR_CAST(tlink);
313 goto free_full_path;
314 }
315 ses = tlink_tcon(tlink)->ses;
316
317 xid = get_xid();
318 rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
319 &num_referrals, &referrals,
320 cifs_remap(cifs_sb));
321 free_xid(xid);
322
323 cifs_put_tlink(tlink);
324
325 mnt = ERR_PTR(-ENOENT);
326 for (i = 0; i < num_referrals; i++) {
327 int len;
328 dump_referral(referrals + i);
329
330 len = strlen(referrals[i].node_name);
331 if (len < 2) {
332 cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
333 __func__, referrals[i].node_name);
334 mnt = ERR_PTR(-EINVAL);
335 break;
336 }
337 mnt = cifs_dfs_do_refmount(cifs_sb,
338 full_path, referrals + i);
339 cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n",
340 __func__, referrals[i].node_name, mnt);
341 if (!IS_ERR(mnt))
342 goto success;
343 }
344
345
346
347 if (rc != 0)
348 mnt = ERR_PTR(rc);
349
350success:
351 free_dfs_info_array(referrals, num_referrals);
352free_full_path:
353 kfree(full_path);
354cdda_exit:
355 cifs_dbg(FYI, "leaving %s\n" , __func__);
356 return mnt;
357}
358
359
360
361
362struct vfsmount *cifs_dfs_d_automount(struct path *path)
363{
364 struct vfsmount *newmnt;
365
366 cifs_dbg(FYI, "in %s\n", __func__);
367
368 newmnt = cifs_dfs_do_automount(path->dentry);
369 if (IS_ERR(newmnt)) {
370 cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
371 return newmnt;
372 }
373
374 mntget(newmnt);
375 mnt_set_expiry(newmnt, &cifs_dfs_automount_list);
376 schedule_delayed_work(&cifs_dfs_automount_task,
377 cifs_dfs_mountpoint_expiry_timeout);
378 cifs_dbg(FYI, "leaving %s [ok]\n" , __func__);
379 return newmnt;
380}
381
382const struct inode_operations cifs_dfs_referral_inode_operations = {
383};
384