Fix bug #5909 - MS-DFS does not work on Vista, if link name includes multibyte character.
[samba.git] / source3 / smbd / msdfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDFS services for Samba
5    Copyright (C) Shirish Kalele 2000
6    Copyright (C) Jeremy Allison 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
26 extern uint32 global_client_caps;
27
28 /**********************************************************************
29  Parse a DFS pathname of the form \hostname\service\reqpath
30  into the dfs_path structure.
31  If POSIX pathnames is true, the pathname may also be of the
32  form /hostname/service/reqpath.
33  We cope with either here.
34
35  Unfortunately, due to broken clients who might set the
36  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
37  send a local path, we have to cope with that too....
38
39  If conn != NULL then ensure the provided service is
40  the one pointed to by the connection.
41
42  This version does everything using pointers within one copy of the
43  pathname string, talloced on the struct dfs_path pointer (which
44  must be talloced). This may be too clever to live....
45  JRA.
46 **********************************************************************/
47
48 static NTSTATUS parse_dfs_path(connection_struct *conn,
49                                 const char *pathname,
50                                 bool allow_wcards,
51                                 struct dfs_path *pdp, /* MUST BE TALLOCED */
52                                 bool *ppath_contains_wcard)
53 {
54         char *pathname_local;
55         char *p,*temp;
56         char *servicename;
57         char *eos_ptr;
58         NTSTATUS status = NT_STATUS_OK;
59         char sepchar;
60
61         ZERO_STRUCTP(pdp);
62
63         /*
64          * This is the only talloc we should need to do
65          * on the struct dfs_path. All the pointers inside
66          * it should point to offsets within this string.
67          */
68
69         pathname_local = talloc_strdup(pdp, pathname);
70         if (!pathname_local) {
71                 return NT_STATUS_NO_MEMORY;
72         }
73         /* Get a pointer to the terminating '\0' */
74         eos_ptr = &pathname_local[strlen(pathname_local)];
75         p = temp = pathname_local;
76
77         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78
79         sepchar = pdp->posix_path ? '/' : '\\';
80
81         if (*pathname != sepchar) {
82                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
83                         pathname, sepchar ));
84                 /*
85                  * Possibly client sent a local path by mistake.
86                  * Try and convert to a local path.
87                  */
88
89                 pdp->hostname = eos_ptr; /* "" */
90                 pdp->servicename = eos_ptr; /* "" */
91
92                 /* We've got no info about separators. */
93                 pdp->posix_path = lp_posix_pathnames();
94                 p = temp;
95                 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
96                         "local path\n",
97                         temp));
98                 goto local_path;
99         }
100
101         /*
102          * Safe to use on talloc'ed string as it only shrinks.
103          * It also doesn't affect the eos_ptr.
104          */
105         trim_char(temp,sepchar,sepchar);
106
107         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
108                 temp, sepchar));
109
110         /* Now tokenize. */
111         /* Parse out hostname. */
112         p = strchr_m(temp,sepchar);
113         if(p == NULL) {
114                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
115                         temp));
116                 /*
117                  * Possibly client sent a local path by mistake.
118                  * Try and convert to a local path.
119                  */
120
121                 pdp->hostname = eos_ptr; /* "" */
122                 pdp->servicename = eos_ptr; /* "" */
123
124                 p = temp;
125                 DEBUG(10,("parse_dfs_path: trying to convert %s "
126                         "to a local path\n",
127                         temp));
128                 goto local_path;
129         }
130         *p = '\0';
131         pdp->hostname = temp;
132
133         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134
135         /* Parse out servicename. */
136         servicename = p+1;
137         p = strchr_m(servicename,sepchar);
138         if (p) {
139                 *p = '\0';
140         }
141
142         /* Is this really our servicename ? */
143         if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
144                         || (strequal(servicename, HOMES_NAME)
145                         && strequal(lp_servicename(SNUM(conn)),
146                                 get_current_username()) )) ) {
147                 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
148                         servicename));
149
150                 /*
151                  * Possibly client sent a local path by mistake.
152                  * Try and convert to a local path.
153                  */
154
155                 pdp->hostname = eos_ptr; /* "" */
156                 pdp->servicename = eos_ptr; /* "" */
157
158                 /* Repair the path - replace the sepchar's
159                    we nulled out */
160                 servicename--;
161                 *servicename = sepchar;
162                 if (p) {
163                         *p = sepchar;
164                 }
165
166                 p = temp;
167                 DEBUG(10,("parse_dfs_path: trying to convert %s "
168                         "to a local path\n",
169                         temp));
170                 goto local_path;
171         }
172
173         pdp->servicename = servicename;
174
175         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
176
177         if(p == NULL) {
178                 /* Client sent self referral \server\share. */
179                 pdp->reqpath = eos_ptr; /* "" */
180                 return NT_STATUS_OK;
181         }
182
183         p++;
184
185   local_path:
186
187         *ppath_contains_wcard = False;
188
189         pdp->reqpath = p;
190
191         /* Rest is reqpath. */
192         if (pdp->posix_path) {
193                 status = check_path_syntax_posix(pdp->reqpath);
194         } else {
195                 if (allow_wcards) {
196                         status = check_path_syntax_wcard(pdp->reqpath,
197                                         ppath_contains_wcard);
198                 } else {
199                         status = check_path_syntax(pdp->reqpath);
200                 }
201         }
202
203         if (!NT_STATUS_IS_OK(status)) {
204                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
205                         p, nt_errstr(status) ));
206                 return status;
207         }
208
209         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
210         return NT_STATUS_OK;
211 }
212
213 /********************************************************
214  Fake up a connection struct for the VFS layer.
215  Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
217
218 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219                                 connection_struct **pconn,
220                                 int snum,
221                                 const char *path,
222                                 char **poldcwd)
223 {
224         connection_struct *conn;
225         char *connpath;
226         char *oldcwd;
227
228         conn = TALLOC_ZERO_P(ctx, connection_struct);
229         if (conn == NULL) {
230                 return NT_STATUS_NO_MEMORY;
231         }
232
233         connpath = talloc_strdup(conn, path);
234         if (!connpath) {
235                 TALLOC_FREE(conn);
236                 return NT_STATUS_NO_MEMORY;
237         }
238         connpath = talloc_string_sub(conn,
239                                 connpath,
240                                 "%S",
241                                 lp_servicename(snum));
242         if (!connpath) {
243                 TALLOC_FREE(conn);
244                 return NT_STATUS_NO_MEMORY;
245         }
246
247         /* needed for smbd_vfs_init() */
248
249         if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
250                 DEBUG(0, ("TALLOC failed\n"));
251                 TALLOC_FREE(conn);
252                 return NT_STATUS_NO_MEMORY;
253         }
254
255         conn->params->service = snum;
256
257         set_conn_connectpath(conn, connpath);
258
259         if (!smbd_vfs_init(conn)) {
260                 NTSTATUS status = map_nt_error_from_unix(errno);
261                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
262                 conn_free_internal(conn);
263                 return status;
264         }
265
266         /*
267          * Windows seems to insist on doing trans2getdfsreferral() calls on
268          * the IPC$ share as the anonymous user. If we try to chdir as that
269          * user we will fail.... WTF ? JRA.
270          */
271
272         oldcwd = vfs_GetWd(ctx, conn);
273         if (oldcwd == NULL) {
274                 NTSTATUS status = map_nt_error_from_unix(errno);
275                 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
276                 conn_free_internal(conn);
277                 return status;
278         }
279
280         if (vfs_ChDir(conn,conn->connectpath) != 0) {
281                 NTSTATUS status = map_nt_error_from_unix(errno);
282                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
283                         "Error was %s\n",
284                         conn->connectpath, strerror(errno) ));
285                 conn_free_internal(conn);
286                 return status;
287         }
288
289         *pconn = conn;
290         *poldcwd = oldcwd;
291
292         return NT_STATUS_OK;
293 }
294
295 /**********************************************************************
296  Parse the contents of a symlink to verify if it is an msdfs referral
297  A valid referral is of the form:
298
299  msdfs:server1\share1,server2\share2
300  msdfs:server1\share1\pathname,server2\share2\pathname
301  msdfs:server1/share1,server2/share2
302  msdfs:server1/share1/pathname,server2/share2/pathname.
303
304  Note that the alternate paths returned here must be of the canonicalized
305  form:
306
307  \server\share or
308  \server\share\path\to\file,
309
310  even in posix path mode. This is because we have no knowledge if the
311  server we're referring to understands posix paths.
312  **********************************************************************/
313
314 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
315                                 const char *target,
316                                 struct referral **preflist,
317                                 int *refcount)
318 {
319         char *temp = NULL;
320         char *prot;
321         char **alt_path = NULL;
322         int count = 0, i;
323         struct referral *reflist;
324         char *saveptr;
325
326         temp = talloc_strdup(ctx, target);
327         if (!temp) {
328                 return False;
329         }
330         prot = strtok_r(temp, ":", &saveptr);
331         if (!prot) {
332                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
333                 return False;
334         }
335
336         alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
337         if (!alt_path) {
338                 return False;
339         }
340
341         /* parse out the alternate paths */
342         while((count<MAX_REFERRAL_COUNT) &&
343               ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
344                 count++;
345         }
346
347         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
348
349         if (count) {
350                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
351                                 struct referral, count);
352                 if(reflist == NULL) {
353                         TALLOC_FREE(alt_path);
354                         return False;
355                 }
356         } else {
357                 reflist = *preflist = NULL;
358         }
359
360         for(i=0;i<count;i++) {
361                 char *p;
362
363                 /* Canonicalize link target.
364                  * Replace all /'s in the path by a \ */
365                 string_replace(alt_path[i], '/', '\\');
366
367                 /* Remove leading '\\'s */
368                 p = alt_path[i];
369                 while (*p && (*p == '\\')) {
370                         p++;
371                 }
372
373                 reflist[i].alternate_path = talloc_asprintf(ctx,
374                                 "\\%s",
375                                 p);
376                 if (!reflist[i].alternate_path) {
377                         return False;
378                 }
379
380                 reflist[i].proximity = 0;
381                 reflist[i].ttl = REFERRAL_TTL;
382                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
383                                         reflist[i].alternate_path));
384         }
385
386         *refcount = count;
387
388         TALLOC_FREE(alt_path);
389         return True;
390 }
391
392 /**********************************************************************
393  Returns true if the unix path is a valid msdfs symlink and also
394  returns the target string from inside the link.
395 **********************************************************************/
396
397 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
398                         connection_struct *conn,
399                         const char *path,
400                         char **pp_link_target,
401                         SMB_STRUCT_STAT *sbufp)
402 {
403         SMB_STRUCT_STAT st;
404         int referral_len = 0;
405         char link_target_buf[7];
406         size_t bufsize = 0;
407         char *link_target = NULL;
408
409         if (pp_link_target) {
410                 bufsize = 1024;
411                 link_target = TALLOC_ARRAY(ctx, char, bufsize);
412                 if (!link_target) {
413                         return False;
414                 }
415                 *pp_link_target = link_target;
416         } else {
417                 bufsize = sizeof(link_target_buf);
418                 link_target = link_target_buf;
419         }
420
421         if (sbufp == NULL) {
422                 sbufp = &st;
423         }
424
425         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
426                 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
427                         path));
428                 goto err;
429         }
430
431         if (!S_ISLNK(sbufp->st_mode)) {
432                 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
433                                         path));
434                 goto err;
435         }
436
437         referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
438         if (referral_len == -1) {
439                 DEBUG(0,("is_msdfs_link_read_target: Error reading "
440                         "msdfs link %s: %s\n",
441                         path, strerror(errno)));
442                 goto err;
443         }
444         link_target[referral_len] = '\0';
445
446         DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
447                                 link_target));
448
449         if (!strnequal(link_target, "msdfs:", 6)) {
450                 goto err;
451         }
452         return True;
453
454   err:
455
456         if (link_target != link_target_buf) {
457                 TALLOC_FREE(link_target);
458         }
459         return False;
460 }
461
462 /**********************************************************************
463  Returns true if the unix path is a valid msdfs symlink.
464 **********************************************************************/
465
466 bool is_msdfs_link(connection_struct *conn,
467                 const char *path,
468                 SMB_STRUCT_STAT *sbufp)
469 {
470         return is_msdfs_link_internal(talloc_tos(),
471                                         conn,
472                                         path,
473                                         NULL,
474                                         sbufp);
475 }
476
477 /*****************************************************************
478  Used by other functions to decide if a dfs path is remote,
479  and to get the list of referred locations for that remote path.
480
481  search_flag: For findfirsts, dfs links themselves are not
482  redirected, but paths beyond the links are. For normal smb calls,
483  even dfs links need to be redirected.
484
485  consumedcntp: how much of the dfs path is being redirected. the client
486  should try the remaining path on the redirected server.
487
488  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
489  link redirect are in targetpath.
490 *****************************************************************/
491
492 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
493                 connection_struct *conn,
494                 const char *dfspath, /* Incoming complete dfs path */
495                 const struct dfs_path *pdp, /* Parsed out
496                                                server+share+extrapath. */
497                 bool search_flag, /* Called from a findfirst ? */
498                 int *consumedcntp,
499                 char **pp_targetpath)
500 {
501         char *p = NULL;
502         char *q = NULL;
503         SMB_STRUCT_STAT sbuf;
504         NTSTATUS status;
505         char *localpath = NULL;
506         char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
507                                   components). */
508
509         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
510                 conn->connectpath, pdp->reqpath));
511
512         /*
513          * Note the unix path conversion here we're doing we can
514          * throw away. We're looking for a symlink for a dfs
515          * resolution, if we don't find it we'll do another
516          * unix_convert later in the codepath.
517          * If we needed to remember what we'd resolved in
518          * dp->reqpath (as the original code did) we'd
519          * copy (localhost, dp->reqpath) on any code
520          * path below that returns True - but I don't
521          * think this is needed. JRA.
522          */
523
524         status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
525                         NULL, &sbuf);
526         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
527                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
528                 return status;
529         }
530
531         /* Optimization - check if we can redirect the whole path. */
532
533         if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
534                 if (search_flag) {
535                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
536                                  "for dfs link %s.\n", dfspath));
537                         return NT_STATUS_OK;
538                 }
539
540                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
541                         "valid dfs link %s.\n", dfspath,
542                         pp_targetpath ? *pp_targetpath : ""));
543
544                 if (consumedcntp) {
545                         *consumedcntp = strlen(dfspath);
546                 }
547                 return NT_STATUS_PATH_NOT_COVERED;
548         }
549
550         /* Prepare to test only for '/' components in the given path,
551          * so if a Windows path replace all '\\' characters with '/'.
552          * For a POSIX DFS path we know all separators are already '/'. */
553
554         canon_dfspath = talloc_strdup(ctx, dfspath);
555         if (!canon_dfspath) {
556                 return NT_STATUS_NO_MEMORY;
557         }
558         if (!pdp->posix_path) {
559                 string_replace(canon_dfspath, '\\', '/');
560         }
561
562         /*
563          * localpath comes out of unix_convert, so it has
564          * no trailing backslash. Make sure that canon_dfspath hasn't either.
565          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
566          */
567
568         trim_char(canon_dfspath,0,'/');
569
570         /*
571          * Redirect if any component in the path is a link.
572          * We do this by walking backwards through the
573          * local path, chopping off the last component
574          * in both the local path and the canonicalized
575          * DFS path. If we hit a DFS link then we're done.
576          */
577
578         p = strrchr_m(localpath, '/');
579         if (consumedcntp) {
580                 q = strrchr_m(canon_dfspath, '/');
581         }
582
583         while (p) {
584                 *p = '\0';
585                 if (q) {
586                         *q = '\0';
587                 }
588
589                 if (is_msdfs_link_internal(ctx, conn,
590                                         localpath, pp_targetpath, NULL)) {
591                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
592                                 "parent %s is dfs link\n", dfspath, localpath));
593
594                         if (consumedcntp) {
595                                 *consumedcntp = strlen(canon_dfspath);
596                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
597                                         "(%d)\n",
598                                         canon_dfspath,
599                                         *consumedcntp));
600                         }
601
602                         return NT_STATUS_PATH_NOT_COVERED;
603                 }
604
605                 /* Step back on the filesystem. */
606                 p = strrchr_m(localpath, '/');
607
608                 if (consumedcntp) {
609                         /* And in the canonicalized dfs path. */
610                         q = strrchr_m(canon_dfspath, '/');
611                 }
612         }
613
614         return NT_STATUS_OK;
615 }
616
617 /*****************************************************************
618  Decides if a dfs pathname should be redirected or not.
619  If not, the pathname is converted to a tcon-relative local unix path
620
621  search_wcard_flag: this flag performs 2 functions both related
622  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
623  for details.
624
625  This function can return NT_STATUS_OK, meaning use the returned path as-is
626  (mapped into a local path).
627  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
628  any other NT_STATUS error which is a genuine error to be
629  returned to the client.
630 *****************************************************************/
631
632 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
633                         connection_struct *conn,
634                         const char *path_in,
635                         bool search_wcard_flag,
636                         char **pp_path_out,
637                         bool *ppath_contains_wcard)
638 {
639         NTSTATUS status;
640         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
641
642         if (!pdp) {
643                 return NT_STATUS_NO_MEMORY;
644         }
645
646         status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
647                         ppath_contains_wcard);
648         if (!NT_STATUS_IS_OK(status)) {
649                 TALLOC_FREE(pdp);
650                 return status;
651         }
652
653         if (pdp->reqpath[0] == '\0') {
654                 TALLOC_FREE(pdp);
655                 *pp_path_out = talloc_strdup(ctx, "");
656                 if (!*pp_path_out) {
657                         return NT_STATUS_NO_MEMORY;
658                 }
659                 DEBUG(5,("dfs_redirect: self-referral.\n"));
660                 return NT_STATUS_OK;
661         }
662
663         /* If dfs pathname for a non-dfs share, convert to tcon-relative
664            path and return OK */
665
666         if (!lp_msdfs_root(SNUM(conn))) {
667                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
668                 TALLOC_FREE(pdp);
669                 if (!*pp_path_out) {
670                         return NT_STATUS_NO_MEMORY;
671                 }
672                 return NT_STATUS_OK;
673         }
674
675         /* If it looked like a local path (zero hostname/servicename)
676          * just treat as a tcon-relative path. */
677
678         if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
679                 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
680                 TALLOC_FREE(pdp);
681                 if (!*pp_path_out) {
682                         return NT_STATUS_NO_MEMORY;
683                 }
684                 return NT_STATUS_OK;
685         }
686
687         if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
688                         || (strequal(pdp->servicename, HOMES_NAME)
689                         && strequal(lp_servicename(SNUM(conn)),
690                                 conn->server_info->sanitized_username) )) ) {
691
692                 /* The given sharename doesn't match this connection. */
693                 TALLOC_FREE(pdp);
694
695                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
696         }
697
698         status = dfs_path_lookup(ctx, conn, path_in, pdp,
699                         search_wcard_flag, NULL, NULL);
700         if (!NT_STATUS_IS_OK(status)) {
701                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
702                         DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
703                 } else {
704                         DEBUG(10,("dfs_redirect: dfs_path_lookup "
705                                 "failed for %s with %s\n",
706                                 path_in, nt_errstr(status) ));
707                 }
708                 return status;
709         }
710
711         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
712
713         /* Form non-dfs tcon-relative path */
714         *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
715         TALLOC_FREE(pdp);
716         if (!*pp_path_out) {
717                 return NT_STATUS_NO_MEMORY;
718         }
719
720         DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
721                                 path_in,
722                                 *pp_path_out));
723
724         return NT_STATUS_OK;
725 }
726
727 /**********************************************************************
728  Return a self referral.
729 **********************************************************************/
730
731 static NTSTATUS self_ref(TALLOC_CTX *ctx,
732                         const char *dfs_path,
733                         struct junction_map *jucn,
734                         int *consumedcntp,
735                         bool *self_referralp)
736 {
737         struct referral *ref;
738
739         *self_referralp = True;
740
741         jucn->referral_count = 1;
742         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
743                 return NT_STATUS_NO_MEMORY;
744         }
745
746         ref->alternate_path = talloc_strdup(ctx, dfs_path);
747         if (!ref->alternate_path) {
748                 return NT_STATUS_NO_MEMORY;
749         }
750         ref->proximity = 0;
751         ref->ttl = REFERRAL_TTL;
752         jucn->referral_list = ref;
753         *consumedcntp = strlen(dfs_path);
754         return NT_STATUS_OK;
755 }
756
757 /**********************************************************************
758  Gets valid referrals for a dfs path and fills up the
759  junction_map structure.
760 **********************************************************************/
761
762 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
763                         const char *dfs_path,
764                         struct junction_map *jucn,
765                         int *consumedcntp,
766                         bool *self_referralp)
767 {
768         struct connection_struct *conn;
769         char *targetpath = NULL;
770         int snum;
771         NTSTATUS status = NT_STATUS_NOT_FOUND;
772         bool dummy;
773         struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
774         char *oldpath;
775
776         if (!pdp) {
777                 return NT_STATUS_NO_MEMORY;
778         }
779
780         *self_referralp = False;
781
782         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
783         if (!NT_STATUS_IS_OK(status)) {
784                 return status;
785         }
786
787         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
788         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
789         if (!jucn->service_name || !jucn->volume_name) {
790                 TALLOC_FREE(pdp);
791                 return NT_STATUS_NO_MEMORY;
792         }
793
794         /* Verify the share is a dfs root */
795         snum = lp_servicenumber(jucn->service_name);
796         if(snum < 0) {
797                 fstring service_name;
798                 fstrcpy(service_name, jucn->service_name);
799                 if ((snum = find_service(service_name)) < 0) {
800                         return NT_STATUS_NOT_FOUND;
801                 }
802                 TALLOC_FREE(jucn->service_name);
803                 jucn->service_name = talloc_strdup(ctx, service_name);
804                 if (!jucn->service_name) {
805                         TALLOC_FREE(pdp);
806                         return NT_STATUS_NO_MEMORY;
807                 }
808         }
809
810         if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
811                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
812                         "a dfs root.\n",
813                         pdp->servicename, dfs_path));
814                 TALLOC_FREE(pdp);
815                 return NT_STATUS_NOT_FOUND;
816         }
817
818         /*
819          * Self referrals are tested with a anonymous IPC connection and
820          * a GET_DFS_REFERRAL call to \\server\share. (which means
821          * dp.reqpath[0] points to an empty string). create_conn_struct cd's
822          * into the directory and will fail if it cannot (as the anonymous
823          * user). Cope with this.
824          */
825
826         if (pdp->reqpath[0] == '\0') {
827                 char *tmp;
828                 struct referral *ref;
829
830                 if (*lp_msdfs_proxy(snum) == '\0') {
831                         TALLOC_FREE(pdp);
832                         return self_ref(ctx,
833                                         dfs_path,
834                                         jucn,
835                                         consumedcntp,
836                                         self_referralp);
837                 }
838
839                 /*
840                  * It's an msdfs proxy share. Redirect to
841                  * the configured target share.
842                  */
843
844                 jucn->referral_count = 1;
845                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
846                         TALLOC_FREE(pdp);
847                         return NT_STATUS_NO_MEMORY;
848                 }
849
850                 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
851                         TALLOC_FREE(pdp);
852                         return NT_STATUS_NO_MEMORY;
853                 }
854
855                 trim_string(tmp, "\\", 0);
856
857                 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
858                 TALLOC_FREE(tmp);
859
860                 if (!ref->alternate_path) {
861                         TALLOC_FREE(pdp);
862                         return NT_STATUS_NO_MEMORY;
863                 }
864
865                 if (pdp->reqpath[0] != '\0') {
866                         ref->alternate_path = talloc_asprintf_append(
867                                         ref->alternate_path,
868                                         "%s",
869                                         pdp->reqpath);
870                         if (!ref->alternate_path) {
871                                 TALLOC_FREE(pdp);
872                                 return NT_STATUS_NO_MEMORY;
873                         }
874                 }
875                 ref->proximity = 0;
876                 ref->ttl = REFERRAL_TTL;
877                 jucn->referral_list = ref;
878                 *consumedcntp = strlen(dfs_path);
879                 TALLOC_FREE(pdp);
880                 return NT_STATUS_OK;
881         }
882
883         status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
884                                     &oldpath);
885         if (!NT_STATUS_IS_OK(status)) {
886                 TALLOC_FREE(pdp);
887                 return status;
888         }
889
890         /* If this is a DFS path dfs_lookup should return
891          * NT_STATUS_PATH_NOT_COVERED. */
892
893         status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
894                         False, consumedcntp, &targetpath);
895
896         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
897                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
898                         dfs_path));
899                 vfs_ChDir(conn, oldpath);
900                 conn_free_internal(conn);
901                 TALLOC_FREE(pdp);
902                 return status;
903         }
904
905         /* We know this is a valid dfs link. Parse the targetpath. */
906         if (!parse_msdfs_symlink(ctx, targetpath,
907                                 &jucn->referral_list,
908                                 &jucn->referral_count)) {
909                 DEBUG(3,("get_referred_path: failed to parse symlink "
910                         "target %s\n", targetpath ));
911                 vfs_ChDir(conn, oldpath);
912                 conn_free_internal(conn);
913                 TALLOC_FREE(pdp);
914                 return NT_STATUS_NOT_FOUND;
915         }
916
917         vfs_ChDir(conn, oldpath);
918         conn_free_internal(conn);
919         TALLOC_FREE(pdp);
920         return NT_STATUS_OK;
921 }
922
923 static int setup_ver2_dfs_referral(const char *pathname,
924                                 char **ppdata,
925                                 struct junction_map *junction,
926                                 bool self_referral)
927 {
928         char* pdata = *ppdata;
929
930         smb_ucs2_t *uni_requestedpath = NULL;
931         int uni_reqpathoffset1,uni_reqpathoffset2;
932         int uni_curroffset;
933         int requestedpathlen=0;
934         int offset;
935         int reply_size = 0;
936         int i=0;
937
938         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
939
940         requestedpathlen = rpcstr_push_talloc(talloc_tos(),
941                                         &uni_requestedpath, pathname);
942         if (uni_requestedpath == NULL || requestedpathlen == 0) {
943                 return -1;
944         }
945
946         if (DEBUGLVL(10)) {
947                 dump_data(0, (unsigned char *)uni_requestedpath,
948                         requestedpathlen);
949         }
950
951         DEBUG(10,("ref count = %u\n",junction->referral_count));
952
953         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
954                         VERSION2_REFERRAL_SIZE * junction->referral_count;
955
956         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
957
958         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
959
960         reply_size = REFERRAL_HEADER_SIZE +
961                         VERSION2_REFERRAL_SIZE*junction->referral_count +
962                         2 * requestedpathlen;
963         DEBUG(10,("reply_size: %u\n",reply_size));
964
965         /* add up the unicode lengths of all the referral paths */
966         for(i=0;i<junction->referral_count;i++) {
967                 DEBUG(10,("referral %u : %s\n",
968                         i,
969                         junction->referral_list[i].alternate_path));
970                 reply_size +=
971                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
972         }
973
974         DEBUG(10,("reply_size = %u\n",reply_size));
975         /* add the unexplained 0x16 bytes */
976         reply_size += 0x16;
977
978         pdata = (char *)SMB_REALLOC(pdata,reply_size);
979         if(pdata == NULL) {
980                 DEBUG(0,("Realloc failed!\n"));
981                 return -1;
982         }
983         *ppdata = pdata;
984
985         /* copy in the dfs requested paths.. required for offset calculations */
986         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
987         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
988
989         /* create the header */
990         SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
991                                                 2 byte null */
992         /* number of referral in this pkt */
993         SSVAL(pdata,2,junction->referral_count);
994         if(self_referral) {
995                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
996         } else {
997                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
998         }
999
1000         offset = 8;
1001         /* add the referral elements */
1002         for(i=0;i<junction->referral_count;i++) {
1003                 struct referral* ref = &junction->referral_list[i];
1004                 int unilen;
1005
1006                 SSVAL(pdata,offset,2); /* version 2 */
1007                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1008                 if(self_referral) {
1009                         SSVAL(pdata,offset+4,1);
1010                 } else {
1011                         SSVAL(pdata,offset+4,0);
1012                 }
1013
1014                 /* ref_flags :use path_consumed bytes? */
1015                 SSVAL(pdata,offset+6,0);
1016                 SIVAL(pdata,offset+8,ref->proximity);
1017                 SIVAL(pdata,offset+12,ref->ttl);
1018
1019                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1020                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1021                 /* copy referred path into current offset */
1022                 unilen = rpcstr_push(pdata+uni_curroffset,
1023                                         ref->alternate_path,
1024                                         reply_size - uni_curroffset,
1025                                         STR_UNICODE);
1026
1027                 SSVAL(pdata,offset+20,uni_curroffset-offset);
1028
1029                 uni_curroffset += unilen;
1030                 offset += VERSION2_REFERRAL_SIZE;
1031         }
1032         /* add in the unexplained 22 (0x16) bytes at the end */
1033         memset(pdata+uni_curroffset,'\0',0x16);
1034         return reply_size;
1035 }
1036
1037 static int setup_ver3_dfs_referral(const char *pathname,
1038                                 char **ppdata,
1039                                 struct junction_map *junction,
1040                                 bool self_referral)
1041 {
1042         char *pdata = *ppdata;
1043
1044         smb_ucs2_t *uni_reqpath = NULL;
1045         int uni_reqpathoffset1, uni_reqpathoffset2;
1046         int uni_curroffset;
1047         int reply_size = 0;
1048
1049         int reqpathlen = 0;
1050         int offset,i=0;
1051
1052         DEBUG(10,("setting up version3 referral\n"));
1053
1054         reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1055         if (uni_reqpath == NULL || reqpathlen == 0) {
1056                 return -1;
1057         }
1058
1059         if (DEBUGLVL(10)) {
1060                 dump_data(0, (unsigned char *)uni_reqpath,
1061                         reqpathlen);
1062         }
1063
1064         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1065                         VERSION3_REFERRAL_SIZE * junction->referral_count;
1066         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1067         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1068
1069         for(i=0;i<junction->referral_count;i++) {
1070                 DEBUG(10,("referral %u : %s\n",
1071                         i,
1072                         junction->referral_list[i].alternate_path));
1073                 reply_size +=
1074                         (strlen(junction->referral_list[i].alternate_path)+1)*2;
1075         }
1076
1077         pdata = (char *)SMB_REALLOC(pdata,reply_size);
1078         if(pdata == NULL) {
1079                 DEBUG(0,("version3 referral setup:"
1080                         "malloc failed for Realloc!\n"));
1081                 return -1;
1082         }
1083         *ppdata = pdata;
1084
1085         /* create the header */
1086         SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1087                                           2 byte null */
1088         SSVAL(pdata,2,junction->referral_count); /* number of referral */
1089         if(self_referral) {
1090                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1091         } else {
1092                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1093         }
1094
1095         /* copy in the reqpaths */
1096         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1097         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1098
1099         offset = 8;
1100         for(i=0;i<junction->referral_count;i++) {
1101                 struct referral* ref = &(junction->referral_list[i]);
1102                 int unilen;
1103
1104                 SSVAL(pdata,offset,3); /* version 3 */
1105                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1106                 if(self_referral) {
1107                         SSVAL(pdata,offset+4,1);
1108                 } else {
1109                         SSVAL(pdata,offset+4,0);
1110                 }
1111
1112                 /* ref_flags :use path_consumed bytes? */
1113                 SSVAL(pdata,offset+6,0);
1114                 SIVAL(pdata,offset+8,ref->ttl);
1115
1116                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1117                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1118                 /* copy referred path into current offset */
1119                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1120                                         reply_size - uni_curroffset,
1121                                         STR_UNICODE | STR_TERMINATE);
1122                 SSVAL(pdata,offset+16,uni_curroffset-offset);
1123                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1124                 memset(pdata+offset+18,'\0',16);
1125
1126                 uni_curroffset += unilen;
1127                 offset += VERSION3_REFERRAL_SIZE;
1128         }
1129         return reply_size;
1130 }
1131
1132 /******************************************************************
1133  Set up the DFS referral for the dfs pathname. This call returns
1134  the amount of the path covered by this server, and where the
1135  client should be redirected to. This is the meat of the
1136  TRANS2_GET_DFS_REFERRAL call.
1137 ******************************************************************/
1138
1139 int setup_dfs_referral(connection_struct *orig_conn,
1140                         const char *dfs_path,
1141                         int max_referral_level,
1142                         char **ppdata, NTSTATUS *pstatus)
1143 {
1144         struct junction_map *junction = NULL;
1145         int consumedcnt = 0;
1146         bool self_referral = False;
1147         int reply_size = 0;
1148         char *pathnamep = NULL;
1149         char *local_dfs_path = NULL;
1150         TALLOC_CTX *ctx;
1151
1152         if (!(ctx=talloc_init("setup_dfs_referral"))) {
1153                 *pstatus = NT_STATUS_NO_MEMORY;
1154                 return -1;
1155         }
1156
1157         /* get the junction entry */
1158         if (!dfs_path) {
1159                 talloc_destroy(ctx);
1160                 *pstatus = NT_STATUS_NOT_FOUND;
1161                 return -1;
1162         }
1163
1164         /*
1165          * Trim pathname sent by client so it begins with only one backslash.
1166          * Two backslashes confuse some dfs clients
1167          */
1168
1169         local_dfs_path = talloc_strdup(ctx,dfs_path);
1170         if (!local_dfs_path) {
1171                 *pstatus = NT_STATUS_NO_MEMORY;
1172                 talloc_destroy(ctx);
1173                 return -1;
1174         }
1175         pathnamep = local_dfs_path;
1176         while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1177                         IS_DIRECTORY_SEP(pathnamep[1])) {
1178                 pathnamep++;
1179         }
1180
1181         junction = TALLOC_ZERO_P(ctx, struct junction_map);
1182         if (!junction) {
1183                 *pstatus = NT_STATUS_NO_MEMORY;
1184                 talloc_destroy(ctx);
1185                 return -1;
1186         }
1187
1188         /* The following call can change cwd. */
1189         *pstatus = get_referred_path(ctx, pathnamep, junction,
1190                         &consumedcnt, &self_referral);
1191         if (!NT_STATUS_IS_OK(*pstatus)) {
1192                 vfs_ChDir(orig_conn,orig_conn->connectpath);
1193                 talloc_destroy(ctx);
1194                 return -1;
1195         }
1196         vfs_ChDir(orig_conn,orig_conn->connectpath);
1197
1198         if (!self_referral) {
1199                 pathnamep[consumedcnt] = '\0';
1200
1201                 if( DEBUGLVL( 3 ) ) {
1202                         int i=0;
1203                         dbgtext("setup_dfs_referral: Path %s to "
1204                                 "alternate path(s):",
1205                                 pathnamep);
1206                         for(i=0;i<junction->referral_count;i++)
1207                                 dbgtext(" %s",
1208                                 junction->referral_list[i].alternate_path);
1209                         dbgtext(".\n");
1210                 }
1211         }
1212
1213         /* create the referral depeding on version */
1214         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1215
1216         if (max_referral_level < 2) {
1217                 max_referral_level = 2;
1218         }
1219         if (max_referral_level > 3) {
1220                 max_referral_level = 3;
1221         }
1222
1223         switch(max_referral_level) {
1224         case 2:
1225                 reply_size = setup_ver2_dfs_referral(pathnamep,
1226                                         ppdata, junction,
1227                                         self_referral);
1228                 break;
1229         case 3:
1230                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1231                                         junction, self_referral);
1232                 break;
1233         default:
1234                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1235                         "version: %d\n",
1236                         max_referral_level));
1237                 talloc_destroy(ctx);
1238                 *pstatus = NT_STATUS_INVALID_LEVEL;
1239                 return -1;
1240         }
1241
1242         if (DEBUGLVL(10)) {
1243                 DEBUGADD(0,("DFS Referral pdata:\n"));
1244                 dump_data(0,(uint8 *)*ppdata,reply_size);
1245         }
1246
1247         talloc_destroy(ctx);
1248         *pstatus = NT_STATUS_OK;
1249         return reply_size;
1250 }
1251
1252 /**********************************************************************
1253  The following functions are called by the NETDFS RPC pipe functions
1254  **********************************************************************/
1255
1256 /*********************************************************************
1257  Creates a junction structure from a DFS pathname
1258 **********************************************************************/
1259
1260 bool create_junction(TALLOC_CTX *ctx,
1261                 const char *dfs_path,
1262                 struct junction_map *jucn)
1263 {
1264         int snum;
1265         bool dummy;
1266         struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1267         NTSTATUS status;
1268
1269         if (!pdp) {
1270                 return False;
1271         }
1272         status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1273         if (!NT_STATUS_IS_OK(status)) {
1274                 return False;
1275         }
1276
1277         /* check if path is dfs : validate first token */
1278         if (!is_myname_or_ipaddr(pdp->hostname)) {
1279                 DEBUG(4,("create_junction: Invalid hostname %s "
1280                         "in dfs path %s\n",
1281                         pdp->hostname, dfs_path));
1282                 TALLOC_FREE(pdp);
1283                 return False;
1284         }
1285
1286         /* Check for a non-DFS share */
1287         snum = lp_servicenumber(pdp->servicename);
1288
1289         if(snum < 0 || !lp_msdfs_root(snum)) {
1290                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1291                         pdp->servicename));
1292                 TALLOC_FREE(pdp);
1293                 return False;
1294         }
1295
1296         jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1297         jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1298         jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1299
1300         TALLOC_FREE(pdp);
1301         if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1302                 return False;
1303         }
1304         return True;
1305 }
1306
1307 /**********************************************************************
1308  Forms a valid Unix pathname from the junction
1309  **********************************************************************/
1310
1311 static bool junction_to_local_path(const struct junction_map *jucn,
1312                                    char **pp_path_out,
1313                                    connection_struct **conn_out,
1314                                    char **oldpath)
1315 {
1316         int snum;
1317         NTSTATUS status;
1318
1319         snum = lp_servicenumber(jucn->service_name);
1320         if(snum < 0) {
1321                 return False;
1322         }
1323         status = create_conn_struct(talloc_tos(), conn_out, snum,
1324                                     lp_pathname(snum), oldpath);
1325         if (!NT_STATUS_IS_OK(status)) {
1326                 return False;
1327         }
1328
1329         *pp_path_out = talloc_asprintf(*conn_out,
1330                         "%s/%s",
1331                         lp_pathname(snum),
1332                         jucn->volume_name);
1333         if (!*pp_path_out) {
1334                 vfs_ChDir(*conn_out, *oldpath);
1335                 conn_free_internal(*conn_out);
1336                 return False;
1337         }
1338         return True;
1339 }
1340
1341 bool create_msdfs_link(const struct junction_map *jucn)
1342 {
1343         char *path = NULL;
1344         char *cwd;
1345         char *msdfs_link = NULL;
1346         connection_struct *conn;
1347         int i=0;
1348         bool insert_comma = False;
1349         bool ret = False;
1350
1351         if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1352                 return False;
1353         }
1354
1355         /* Form the msdfs_link contents */
1356         msdfs_link = talloc_strdup(conn, "msdfs:");
1357         if (!msdfs_link) {
1358                 goto out;
1359         }
1360         for(i=0; i<jucn->referral_count; i++) {
1361                 char *refpath = jucn->referral_list[i].alternate_path;
1362
1363                 /* Alternate paths always use Windows separators. */
1364                 trim_char(refpath, '\\', '\\');
1365                 if(*refpath == '\0') {
1366                         if (i == 0) {
1367                                 insert_comma = False;
1368                         }
1369                         continue;
1370                 }
1371                 if (i > 0 && insert_comma) {
1372                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1373                                         ",%s",
1374                                         refpath);
1375                 } else {
1376                         msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1377                                         "%s",
1378                                         refpath);
1379                 }
1380
1381                 if (!msdfs_link) {
1382                         goto out;
1383                 }
1384                 if (!insert_comma) {
1385                         insert_comma = True;
1386                 }
1387         }
1388
1389         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1390                 path, msdfs_link));
1391
1392         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1393                 if (errno == EEXIST) {
1394                         if(SMB_VFS_UNLINK(conn,path)!=0) {
1395                                 goto out;
1396                         }
1397                 }
1398                 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1399                         DEBUG(1,("create_msdfs_link: symlink failed "
1400                                  "%s -> %s\nError: %s\n",
1401                                  path, msdfs_link, strerror(errno)));
1402                         goto out;
1403                 }
1404         }
1405
1406         ret = True;
1407
1408 out:
1409         vfs_ChDir(conn, cwd);
1410         conn_free_internal(conn);
1411         return ret;
1412 }
1413
1414 bool remove_msdfs_link(const struct junction_map *jucn)
1415 {
1416         char *path = NULL;
1417         char *cwd;
1418         connection_struct *conn;
1419         bool ret = False;
1420
1421         if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1422                 return false;
1423         }
1424
1425         if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1426                 ret = True;
1427         }
1428
1429         vfs_ChDir(conn, cwd);
1430         conn_free_internal(conn);
1431         return ret;
1432 }
1433
1434 /*********************************************************************
1435  Return the number of DFS links at the root of this share.
1436 *********************************************************************/
1437
1438 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1439 {
1440         size_t cnt = 0;
1441         SMB_STRUCT_DIR *dirp = NULL;
1442         char *dname = NULL;
1443         const char *connect_path = lp_pathname(snum);
1444         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1445         connection_struct *conn;
1446         NTSTATUS status;
1447         char *cwd;
1448
1449         if(*connect_path == '\0') {
1450                 return 0;
1451         }
1452
1453         /*
1454          * Fake up a connection struct for the VFS layer.
1455          */
1456
1457         status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1458                                     &cwd);
1459         if (!NT_STATUS_IS_OK(status)) {
1460                 DEBUG(3, ("create_conn_struct failed: %s\n",
1461                           nt_errstr(status)));
1462                 return 0;
1463         }
1464
1465         /* Count a link for the msdfs root - convention */
1466         cnt = 1;
1467
1468         /* No more links if this is an msdfs proxy. */
1469         if (*msdfs_proxy != '\0') {
1470                 goto out;
1471         }
1472
1473         /* Now enumerate all dfs links */
1474         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1475         if(!dirp) {
1476                 goto out;
1477         }
1478
1479         while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1480                 if (is_msdfs_link(conn,
1481                                 dname,
1482                                 NULL)) {
1483                         cnt++;
1484                 }
1485         }
1486
1487         SMB_VFS_CLOSEDIR(conn,dirp);
1488
1489 out:
1490         vfs_ChDir(conn, cwd);
1491         conn_free_internal(conn);
1492         return cnt;
1493 }
1494
1495 /*********************************************************************
1496 *********************************************************************/
1497
1498 static int form_junctions(TALLOC_CTX *ctx,
1499                                 int snum,
1500                                 struct junction_map *jucn,
1501                                 size_t jn_remain)
1502 {
1503         size_t cnt = 0;
1504         SMB_STRUCT_DIR *dirp = NULL;
1505         char *dname = NULL;
1506         const char *connect_path = lp_pathname(snum);
1507         char *service_name = lp_servicename(snum);
1508         const char *msdfs_proxy = lp_msdfs_proxy(snum);
1509         connection_struct *conn;
1510         struct referral *ref = NULL;
1511         char *cwd;
1512         NTSTATUS status;
1513
1514         if (jn_remain == 0) {
1515                 return 0;
1516         }
1517
1518         if(*connect_path == '\0') {
1519                 return 0;
1520         }
1521
1522         /*
1523          * Fake up a connection struct for the VFS layer.
1524          */
1525
1526         status = create_conn_struct(ctx, &conn, snum, connect_path, &cwd);
1527         if (!NT_STATUS_IS_OK(status)) {
1528                 DEBUG(3, ("create_conn_struct failed: %s\n",
1529                           nt_errstr(status)));
1530                 return 0;
1531         }
1532
1533         /* form a junction for the msdfs root - convention
1534            DO NOT REMOVE THIS: NT clients will not work with us
1535            if this is not present
1536         */
1537         jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1538         jucn[cnt].volume_name = talloc_strdup(ctx, "");
1539         if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1540                 goto out;
1541         }
1542         jucn[cnt].comment = "";
1543         jucn[cnt].referral_count = 1;
1544
1545         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1546         if (jucn[cnt].referral_list == NULL) {
1547                 goto out;
1548         }
1549
1550         ref->proximity = 0;
1551         ref->ttl = REFERRAL_TTL;
1552         if (*msdfs_proxy != '\0') {
1553                 ref->alternate_path = talloc_strdup(ctx,
1554                                                 msdfs_proxy);
1555         } else {
1556                 ref->alternate_path = talloc_asprintf(ctx,
1557                         "\\\\%s\\%s",
1558                         get_local_machine_name(),
1559                         service_name);
1560         }
1561
1562         if (!ref->alternate_path) {
1563                 goto out;
1564         }
1565         cnt++;
1566
1567         /* Don't enumerate if we're an msdfs proxy. */
1568         if (*msdfs_proxy != '\0') {
1569                 goto out;
1570         }
1571
1572         /* Now enumerate all dfs links */
1573         dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1574         if(!dirp) {
1575                 goto out;
1576         }
1577
1578         while ((dname = vfs_readdirname(conn, dirp)) != NULL) {
1579                 char *link_target = NULL;
1580                 if (cnt >= jn_remain) {
1581                         DEBUG(2, ("form_junctions: ran out of MSDFS "
1582                                 "junction slots"));
1583                         goto out;
1584                 }
1585                 if (is_msdfs_link_internal(ctx,
1586                                         conn,
1587                                         dname, &link_target,
1588                                         NULL)) {
1589                         if (parse_msdfs_symlink(ctx,
1590                                         link_target,
1591                                         &jucn[cnt].referral_list,
1592                                         &jucn[cnt].referral_count)) {
1593
1594                                 jucn[cnt].service_name = talloc_strdup(ctx,
1595                                                                 service_name);
1596                                 jucn[cnt].volume_name = talloc_strdup(ctx,
1597                                                                 dname);
1598                                 if (!jucn[cnt].service_name ||
1599                                                 !jucn[cnt].volume_name) {
1600                                         goto out;
1601                                 }
1602                                 jucn[cnt].comment = "";
1603                                 cnt++;
1604                         }
1605                         TALLOC_FREE(link_target);
1606                 }
1607         }
1608
1609 out:
1610
1611         if (dirp) {
1612                 SMB_VFS_CLOSEDIR(conn,dirp);
1613         }
1614
1615         vfs_ChDir(conn, cwd);
1616         conn_free_internal(conn);
1617         return cnt;
1618 }
1619
1620 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1621 {
1622         struct junction_map *jn = NULL;
1623         int i=0;
1624         size_t jn_count = 0;
1625         int sharecount = 0;
1626
1627         *p_num_jn = 0;
1628         if(!lp_host_msdfs()) {
1629                 return NULL;
1630         }
1631
1632         /* Ensure all the usershares are loaded. */
1633         become_root();
1634         load_registry_shares();
1635         sharecount = load_usershare_shares();
1636         unbecome_root();
1637
1638         for(i=0;i < sharecount;i++) {
1639                 if(lp_msdfs_root(i)) {
1640                         jn_count += count_dfs_links(ctx, i);
1641                 }
1642         }
1643         if (jn_count == 0) {
1644                 return NULL;
1645         }
1646         jn = TALLOC_ARRAY(ctx,  struct junction_map, jn_count);
1647         if (!jn) {
1648                 return NULL;
1649         }
1650         for(i=0; i < sharecount; i++) {
1651                 if (*p_num_jn >= jn_count) {
1652                         break;
1653                 }
1654                 if(lp_msdfs_root(i)) {
1655                         *p_num_jn += form_junctions(ctx, i,
1656                                         &jn[*p_num_jn],
1657                                         jn_count - *p_num_jn);
1658                 }
1659         }
1660         return jn;
1661 }
1662
1663 /******************************************************************************
1664  Core function to resolve a dfs pathname.
1665 ******************************************************************************/
1666
1667 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1668                         connection_struct *conn,
1669                         bool dfs_pathnames,
1670                         const char *name_in,
1671                         char **pp_name_out)
1672 {
1673         NTSTATUS status = NT_STATUS_OK;
1674         bool dummy;
1675         if (dfs_pathnames) {
1676                 status = dfs_redirect(ctx,
1677                                         conn,
1678                                         name_in,
1679                                         False,
1680                                         pp_name_out,
1681                                         &dummy);
1682         } else {
1683                 /*
1684                  * Cheat and just return a copy of the in ptr.
1685                  * Once srvstr_get_path() uses talloc it'll
1686                  * be a talloced ptr anyway.
1687                  */
1688                 *pp_name_out = CONST_DISCARD(char *,name_in);
1689         }
1690         return status;
1691 }
1692
1693 /******************************************************************************
1694  Core function to resolve a dfs pathname possibly containing a wildcard.
1695  This function is identical to the above except for the bool param to
1696  dfs_redirect but I need this to be separate so it's really clear when
1697  we're allowing wildcards and when we're not. JRA.
1698 ******************************************************************************/
1699
1700 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1701                                 connection_struct *conn,
1702                                 bool dfs_pathnames,
1703                                 const char *name_in,
1704                                 char **pp_name_out,
1705                                 bool *ppath_contains_wcard)
1706 {
1707         NTSTATUS status = NT_STATUS_OK;
1708         if (dfs_pathnames) {
1709                 status = dfs_redirect(ctx,
1710                                         conn,
1711                                         name_in,
1712                                         True,
1713                                         pp_name_out,
1714                                         ppath_contains_wcard);
1715         } else {
1716                 /*
1717                  * Cheat and just return a copy of the in ptr.
1718                  * Once srvstr_get_path() uses talloc it'll
1719                  * be a talloced ptr anyway.
1720                  */
1721                 *pp_name_out = CONST_DISCARD(char *,name_in);
1722         }
1723         return status;
1724 }