Make DFS work over SMB2.
[Samba/wip.git] / source3 / smbd / msdfs.c
blob92c3e0ebad4c3bbb7f9c1001600f627f44d8ee58
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
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.
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.
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/>.
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25 #include "smbd/globals.h"
27 /**********************************************************************
28 Parse a DFS pathname of the form \hostname\service\reqpath
29 into the dfs_path structure.
30 If POSIX pathnames is true, the pathname may also be of the
31 form /hostname/service/reqpath.
32 We cope with either here.
34 Unfortunately, due to broken clients who might set the
35 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
36 send a local path, we have to cope with that too....
38 If conn != NULL then ensure the provided service is
39 the one pointed to by the connection.
41 This version does everything using pointers within one copy of the
42 pathname string, talloced on the struct dfs_path pointer (which
43 must be talloced). This may be too clever to live....
44 JRA.
45 **********************************************************************/
47 static NTSTATUS parse_dfs_path(connection_struct *conn,
48 const char *pathname,
49 bool allow_wcards,
50 struct dfs_path *pdp, /* MUST BE TALLOCED */
51 bool *ppath_contains_wcard)
53 struct smbd_server_connection *sconn = smbd_server_conn;
54 char *pathname_local;
55 char *p,*temp;
56 char *servicename;
57 char *eos_ptr;
58 NTSTATUS status = NT_STATUS_OK;
59 char sepchar;
61 ZERO_STRUCTP(pdp);
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.
69 pathname_local = talloc_strdup(pdp, pathname);
70 if (!pathname_local) {
71 return NT_STATUS_NO_MEMORY;
73 /* Get a pointer to the terminating '\0' */
74 eos_ptr = &pathname_local[strlen(pathname_local)];
75 p = temp = pathname_local;
77 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
79 sepchar = pdp->posix_path ? '/' : '\\';
81 if (!sconn->allow_smb2 && (*pathname != sepchar)) {
82 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
83 pathname, sepchar ));
85 * Possibly client sent a local path by mistake.
86 * Try and convert to a local path.
89 pdp->hostname = eos_ptr; /* "" */
90 pdp->servicename = eos_ptr; /* "" */
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;
102 * Safe to use on talloc'ed string as it only shrinks.
103 * It also doesn't affect the eos_ptr.
105 trim_char(temp,sepchar,sepchar);
107 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
108 temp, sepchar));
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));
117 * Possibly client sent a local path by mistake.
118 * Try and convert to a local path.
121 pdp->hostname = eos_ptr; /* "" */
122 pdp->servicename = eos_ptr; /* "" */
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;
130 *p = '\0';
131 pdp->hostname = temp;
133 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
135 /* Parse out servicename. */
136 servicename = p+1;
137 p = strchr_m(servicename,sepchar);
138 if (p) {
139 *p = '\0';
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));
151 * Possibly client sent a local path by mistake.
152 * Try and convert to a local path.
155 pdp->hostname = eos_ptr; /* "" */
156 pdp->servicename = eos_ptr; /* "" */
158 /* Repair the path - replace the sepchar's
159 we nulled out */
160 servicename--;
161 *servicename = sepchar;
162 if (p) {
163 *p = sepchar;
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;
173 pdp->servicename = servicename;
175 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
177 if(p == NULL) {
178 /* Client sent self referral \server\share. */
179 pdp->reqpath = eos_ptr; /* "" */
180 return NT_STATUS_OK;
183 p++;
185 local_path:
187 *ppath_contains_wcard = False;
189 pdp->reqpath = p;
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);
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;
209 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
210 return NT_STATUS_OK;
213 /********************************************************
214 Fake up a connection struct for the VFS layer.
215 Note this CHANGES CWD !!!! JRA.
216 *********************************************************/
218 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
219 connection_struct **pconn,
220 int snum,
221 const char *path,
222 struct auth_serversupplied_info *server_info,
223 char **poldcwd)
225 connection_struct *conn;
226 char *connpath;
227 char *oldcwd;
229 conn = TALLOC_ZERO_P(ctx, connection_struct);
230 if (conn == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 connpath = talloc_strdup(conn, path);
235 if (!connpath) {
236 TALLOC_FREE(conn);
237 return NT_STATUS_NO_MEMORY;
239 connpath = talloc_string_sub(conn,
240 connpath,
241 "%S",
242 lp_servicename(snum));
243 if (!connpath) {
244 TALLOC_FREE(conn);
245 return NT_STATUS_NO_MEMORY;
248 /* needed for smbd_vfs_init() */
250 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
251 DEBUG(0, ("TALLOC failed\n"));
252 TALLOC_FREE(conn);
253 return NT_STATUS_NO_MEMORY;
256 conn->params->service = snum;
258 if (server_info != NULL) {
259 conn->server_info = copy_serverinfo(conn, server_info);
260 if (conn->server_info == NULL) {
261 DEBUG(0, ("copy_serverinfo failed\n"));
262 TALLOC_FREE(conn);
263 return NT_STATUS_NO_MEMORY;
267 set_conn_connectpath(conn, connpath);
269 if (!smbd_vfs_init(conn)) {
270 NTSTATUS status = map_nt_error_from_unix(errno);
271 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
272 conn_free(conn);
273 return status;
276 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
279 * Windows seems to insist on doing trans2getdfsreferral() calls on
280 * the IPC$ share as the anonymous user. If we try to chdir as that
281 * user we will fail.... WTF ? JRA.
284 oldcwd = vfs_GetWd(ctx, conn);
285 if (oldcwd == NULL) {
286 NTSTATUS status = map_nt_error_from_unix(errno);
287 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
288 conn_free(conn);
289 return status;
292 if (vfs_ChDir(conn,conn->connectpath) != 0) {
293 NTSTATUS status = map_nt_error_from_unix(errno);
294 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
295 "Error was %s\n",
296 conn->connectpath, strerror(errno) ));
297 conn_free(conn);
298 return status;
301 *pconn = conn;
302 *poldcwd = oldcwd;
304 return NT_STATUS_OK;
307 /**********************************************************************
308 Parse the contents of a symlink to verify if it is an msdfs referral
309 A valid referral is of the form:
311 msdfs:server1\share1,server2\share2
312 msdfs:server1\share1\pathname,server2\share2\pathname
313 msdfs:server1/share1,server2/share2
314 msdfs:server1/share1/pathname,server2/share2/pathname.
316 Note that the alternate paths returned here must be of the canonicalized
317 form:
319 \server\share or
320 \server\share\path\to\file,
322 even in posix path mode. This is because we have no knowledge if the
323 server we're referring to understands posix paths.
324 **********************************************************************/
326 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
327 const char *target,
328 struct referral **preflist,
329 int *refcount)
331 char *temp = NULL;
332 char *prot;
333 char **alt_path = NULL;
334 int count = 0, i;
335 struct referral *reflist;
336 char *saveptr;
338 temp = talloc_strdup(ctx, target);
339 if (!temp) {
340 return False;
342 prot = strtok_r(temp, ":", &saveptr);
343 if (!prot) {
344 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
345 return False;
348 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
349 if (!alt_path) {
350 return False;
353 /* parse out the alternate paths */
354 while((count<MAX_REFERRAL_COUNT) &&
355 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
356 count++;
359 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
361 if (count) {
362 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
363 struct referral, count);
364 if(reflist == NULL) {
365 TALLOC_FREE(alt_path);
366 return False;
368 } else {
369 reflist = *preflist = NULL;
372 for(i=0;i<count;i++) {
373 char *p;
375 /* Canonicalize link target.
376 * Replace all /'s in the path by a \ */
377 string_replace(alt_path[i], '/', '\\');
379 /* Remove leading '\\'s */
380 p = alt_path[i];
381 while (*p && (*p == '\\')) {
382 p++;
385 reflist[i].alternate_path = talloc_asprintf(ctx,
386 "\\%s",
388 if (!reflist[i].alternate_path) {
389 return False;
392 reflist[i].proximity = 0;
393 reflist[i].ttl = REFERRAL_TTL;
394 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
395 reflist[i].alternate_path));
398 *refcount = count;
400 TALLOC_FREE(alt_path);
401 return True;
404 /**********************************************************************
405 Returns true if the unix path is a valid msdfs symlink and also
406 returns the target string from inside the link.
407 **********************************************************************/
409 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
410 connection_struct *conn,
411 const char *path,
412 char **pp_link_target,
413 SMB_STRUCT_STAT *sbufp)
415 int referral_len = 0;
416 #if defined(HAVE_BROKEN_READLINK)
417 char link_target_buf[PATH_MAX];
418 #else
419 char link_target_buf[7];
420 #endif
421 size_t bufsize = 0;
422 char *link_target = NULL;
423 struct smb_filename smb_fname;
425 if (pp_link_target) {
426 bufsize = 1024;
427 link_target = TALLOC_ARRAY(ctx, char, bufsize);
428 if (!link_target) {
429 return False;
431 *pp_link_target = link_target;
432 } else {
433 bufsize = sizeof(link_target_buf);
434 link_target = link_target_buf;
437 ZERO_STRUCT(smb_fname);
438 smb_fname.base_name = discard_const_p(char, path);
440 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
441 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
442 path));
443 goto err;
445 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
446 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
447 path));
448 goto err;
450 if (sbufp != NULL) {
451 *sbufp = smb_fname.st;
454 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
455 if (referral_len == -1) {
456 DEBUG(0,("is_msdfs_link_read_target: Error reading "
457 "msdfs link %s: %s\n",
458 path, strerror(errno)));
459 goto err;
461 link_target[referral_len] = '\0';
463 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
464 link_target));
466 if (!strnequal(link_target, "msdfs:", 6)) {
467 goto err;
469 return True;
471 err:
473 if (link_target != link_target_buf) {
474 TALLOC_FREE(link_target);
476 return False;
479 /**********************************************************************
480 Returns true if the unix path is a valid msdfs symlink.
481 **********************************************************************/
483 bool is_msdfs_link(connection_struct *conn,
484 const char *path,
485 SMB_STRUCT_STAT *sbufp)
487 return is_msdfs_link_internal(talloc_tos(),
488 conn,
489 path,
490 NULL,
491 sbufp);
494 /*****************************************************************
495 Used by other functions to decide if a dfs path is remote,
496 and to get the list of referred locations for that remote path.
498 search_flag: For findfirsts, dfs links themselves are not
499 redirected, but paths beyond the links are. For normal smb calls,
500 even dfs links need to be redirected.
502 consumedcntp: how much of the dfs path is being redirected. the client
503 should try the remaining path on the redirected server.
505 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
506 link redirect are in targetpath.
507 *****************************************************************/
509 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
510 connection_struct *conn,
511 const char *dfspath, /* Incoming complete dfs path */
512 const struct dfs_path *pdp, /* Parsed out
513 server+share+extrapath. */
514 bool search_flag, /* Called from a findfirst ? */
515 int *consumedcntp,
516 char **pp_targetpath)
518 char *p = NULL;
519 char *q = NULL;
520 NTSTATUS status;
521 struct smb_filename *smb_fname = NULL;
522 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
523 components). */
525 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
526 conn->connectpath, pdp->reqpath));
529 * Note the unix path conversion here we're doing we can
530 * throw away. We're looking for a symlink for a dfs
531 * resolution, if we don't find it we'll do another
532 * unix_convert later in the codepath.
533 * If we needed to remember what we'd resolved in
534 * dp->reqpath (as the original code did) we'd
535 * copy (localhost, dp->reqpath) on any code
536 * path below that returns True - but I don't
537 * think this is needed. JRA.
540 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
541 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
543 if (!NT_STATUS_IS_OK(status)) {
544 if (!NT_STATUS_EQUAL(status,
545 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
546 return status;
549 /* Create an smb_fname to use below. */
550 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
551 NULL, &smb_fname);
552 if (!NT_STATUS_IS_OK(status)) {
553 return status;
557 /* Optimization - check if we can redirect the whole path. */
559 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
560 pp_targetpath, NULL)) {
561 if (search_flag) {
562 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
563 "for dfs link %s.\n", dfspath));
564 status = NT_STATUS_OK;
565 goto out;
568 DEBUG(6,("dfs_path_lookup: %s resolves to a "
569 "valid dfs link %s.\n", dfspath,
570 pp_targetpath ? *pp_targetpath : ""));
572 if (consumedcntp) {
573 *consumedcntp = strlen(dfspath);
575 status = NT_STATUS_PATH_NOT_COVERED;
576 goto out;
579 /* Prepare to test only for '/' components in the given path,
580 * so if a Windows path replace all '\\' characters with '/'.
581 * For a POSIX DFS path we know all separators are already '/'. */
583 canon_dfspath = talloc_strdup(ctx, dfspath);
584 if (!canon_dfspath) {
585 status = NT_STATUS_NO_MEMORY;
586 goto out;
588 if (!pdp->posix_path) {
589 string_replace(canon_dfspath, '\\', '/');
593 * localpath comes out of unix_convert, so it has
594 * no trailing backslash. Make sure that canon_dfspath hasn't either.
595 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
598 trim_char(canon_dfspath,0,'/');
601 * Redirect if any component in the path is a link.
602 * We do this by walking backwards through the
603 * local path, chopping off the last component
604 * in both the local path and the canonicalized
605 * DFS path. If we hit a DFS link then we're done.
608 p = strrchr_m(smb_fname->base_name, '/');
609 if (consumedcntp) {
610 q = strrchr_m(canon_dfspath, '/');
613 while (p) {
614 *p = '\0';
615 if (q) {
616 *q = '\0';
619 if (is_msdfs_link_internal(ctx, conn,
620 smb_fname->base_name, pp_targetpath,
621 NULL)) {
622 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
623 "parent %s is dfs link\n", dfspath,
624 smb_fname_str_dbg(smb_fname)));
626 if (consumedcntp) {
627 *consumedcntp = strlen(canon_dfspath);
628 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
629 "(%d)\n",
630 canon_dfspath,
631 *consumedcntp));
634 status = NT_STATUS_PATH_NOT_COVERED;
635 goto out;
638 /* Step back on the filesystem. */
639 p = strrchr_m(smb_fname->base_name, '/');
641 if (consumedcntp) {
642 /* And in the canonicalized dfs path. */
643 q = strrchr_m(canon_dfspath, '/');
647 status = NT_STATUS_OK;
648 out:
649 TALLOC_FREE(smb_fname);
650 return status;
653 /*****************************************************************
654 Decides if a dfs pathname should be redirected or not.
655 If not, the pathname is converted to a tcon-relative local unix path
657 search_wcard_flag: this flag performs 2 functions both related
658 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
659 for details.
661 This function can return NT_STATUS_OK, meaning use the returned path as-is
662 (mapped into a local path).
663 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
664 any other NT_STATUS error which is a genuine error to be
665 returned to the client.
666 *****************************************************************/
668 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
669 connection_struct *conn,
670 const char *path_in,
671 bool search_wcard_flag,
672 char **pp_path_out,
673 bool *ppath_contains_wcard)
675 NTSTATUS status;
676 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
678 if (!pdp) {
679 return NT_STATUS_NO_MEMORY;
682 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
683 ppath_contains_wcard);
684 if (!NT_STATUS_IS_OK(status)) {
685 TALLOC_FREE(pdp);
686 return status;
689 if (pdp->reqpath[0] == '\0') {
690 TALLOC_FREE(pdp);
691 *pp_path_out = talloc_strdup(ctx, "");
692 if (!*pp_path_out) {
693 return NT_STATUS_NO_MEMORY;
695 DEBUG(5,("dfs_redirect: self-referral.\n"));
696 return NT_STATUS_OK;
699 /* If dfs pathname for a non-dfs share, convert to tcon-relative
700 path and return OK */
702 if (!lp_msdfs_root(SNUM(conn))) {
703 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
704 TALLOC_FREE(pdp);
705 if (!*pp_path_out) {
706 return NT_STATUS_NO_MEMORY;
708 return NT_STATUS_OK;
711 /* If it looked like a local path (zero hostname/servicename)
712 * just treat as a tcon-relative path. */
714 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
715 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
716 TALLOC_FREE(pdp);
717 if (!*pp_path_out) {
718 return NT_STATUS_NO_MEMORY;
720 return NT_STATUS_OK;
723 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
724 || (strequal(pdp->servicename, HOMES_NAME)
725 && strequal(lp_servicename(SNUM(conn)),
726 conn->server_info->sanitized_username) )) ) {
728 /* The given sharename doesn't match this connection. */
729 TALLOC_FREE(pdp);
731 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
734 status = dfs_path_lookup(ctx, conn, path_in, pdp,
735 search_wcard_flag, NULL, NULL);
736 if (!NT_STATUS_IS_OK(status)) {
737 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
738 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
739 } else {
740 DEBUG(10,("dfs_redirect: dfs_path_lookup "
741 "failed for %s with %s\n",
742 path_in, nt_errstr(status) ));
744 return status;
747 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
749 /* Form non-dfs tcon-relative path */
750 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
751 TALLOC_FREE(pdp);
752 if (!*pp_path_out) {
753 return NT_STATUS_NO_MEMORY;
756 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
757 path_in,
758 *pp_path_out));
760 return NT_STATUS_OK;
763 /**********************************************************************
764 Return a self referral.
765 **********************************************************************/
767 static NTSTATUS self_ref(TALLOC_CTX *ctx,
768 const char *dfs_path,
769 struct junction_map *jucn,
770 int *consumedcntp,
771 bool *self_referralp)
773 struct referral *ref;
775 *self_referralp = True;
777 jucn->referral_count = 1;
778 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
779 return NT_STATUS_NO_MEMORY;
782 ref->alternate_path = talloc_strdup(ctx, dfs_path);
783 if (!ref->alternate_path) {
784 return NT_STATUS_NO_MEMORY;
786 ref->proximity = 0;
787 ref->ttl = REFERRAL_TTL;
788 jucn->referral_list = ref;
789 *consumedcntp = strlen(dfs_path);
790 return NT_STATUS_OK;
793 /**********************************************************************
794 Gets valid referrals for a dfs path and fills up the
795 junction_map structure.
796 **********************************************************************/
798 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
799 const char *dfs_path,
800 struct junction_map *jucn,
801 int *consumedcntp,
802 bool *self_referralp)
804 struct connection_struct *conn;
805 char *targetpath = NULL;
806 int snum;
807 NTSTATUS status = NT_STATUS_NOT_FOUND;
808 bool dummy;
809 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
810 char *oldpath;
812 if (!pdp) {
813 return NT_STATUS_NO_MEMORY;
816 *self_referralp = False;
818 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
819 if (!NT_STATUS_IS_OK(status)) {
820 return status;
823 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
824 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
825 if (!jucn->service_name || !jucn->volume_name) {
826 TALLOC_FREE(pdp);
827 return NT_STATUS_NO_MEMORY;
830 /* Verify the share is a dfs root */
831 snum = lp_servicenumber(jucn->service_name);
832 if(snum < 0) {
833 fstring service_name;
834 fstrcpy(service_name, jucn->service_name);
835 if ((snum = find_service(service_name)) < 0) {
836 return NT_STATUS_NOT_FOUND;
838 TALLOC_FREE(jucn->service_name);
839 jucn->service_name = talloc_strdup(ctx, service_name);
840 if (!jucn->service_name) {
841 TALLOC_FREE(pdp);
842 return NT_STATUS_NO_MEMORY;
846 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
847 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
848 "a dfs root.\n",
849 pdp->servicename, dfs_path));
850 TALLOC_FREE(pdp);
851 return NT_STATUS_NOT_FOUND;
855 * Self referrals are tested with a anonymous IPC connection and
856 * a GET_DFS_REFERRAL call to \\server\share. (which means
857 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
858 * into the directory and will fail if it cannot (as the anonymous
859 * user). Cope with this.
862 if (pdp->reqpath[0] == '\0') {
863 char *tmp;
864 struct referral *ref;
866 if (*lp_msdfs_proxy(snum) == '\0') {
867 TALLOC_FREE(pdp);
868 return self_ref(ctx,
869 dfs_path,
870 jucn,
871 consumedcntp,
872 self_referralp);
876 * It's an msdfs proxy share. Redirect to
877 * the configured target share.
880 jucn->referral_count = 1;
881 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
882 TALLOC_FREE(pdp);
883 return NT_STATUS_NO_MEMORY;
886 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
887 TALLOC_FREE(pdp);
888 return NT_STATUS_NO_MEMORY;
891 trim_string(tmp, "\\", 0);
893 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
894 TALLOC_FREE(tmp);
896 if (!ref->alternate_path) {
897 TALLOC_FREE(pdp);
898 return NT_STATUS_NO_MEMORY;
901 if (pdp->reqpath[0] != '\0') {
902 ref->alternate_path = talloc_asprintf_append(
903 ref->alternate_path,
904 "%s",
905 pdp->reqpath);
906 if (!ref->alternate_path) {
907 TALLOC_FREE(pdp);
908 return NT_STATUS_NO_MEMORY;
911 ref->proximity = 0;
912 ref->ttl = REFERRAL_TTL;
913 jucn->referral_list = ref;
914 *consumedcntp = strlen(dfs_path);
915 TALLOC_FREE(pdp);
916 return NT_STATUS_OK;
919 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
920 NULL, &oldpath);
921 if (!NT_STATUS_IS_OK(status)) {
922 TALLOC_FREE(pdp);
923 return status;
926 /* If this is a DFS path dfs_lookup should return
927 * NT_STATUS_PATH_NOT_COVERED. */
929 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
930 False, consumedcntp, &targetpath);
932 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
933 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
934 dfs_path));
935 vfs_ChDir(conn, oldpath);
936 conn_free(conn);
937 TALLOC_FREE(pdp);
938 return status;
941 /* We know this is a valid dfs link. Parse the targetpath. */
942 if (!parse_msdfs_symlink(ctx, targetpath,
943 &jucn->referral_list,
944 &jucn->referral_count)) {
945 DEBUG(3,("get_referred_path: failed to parse symlink "
946 "target %s\n", targetpath ));
947 vfs_ChDir(conn, oldpath);
948 conn_free(conn);
949 TALLOC_FREE(pdp);
950 return NT_STATUS_NOT_FOUND;
953 vfs_ChDir(conn, oldpath);
954 conn_free(conn);
955 TALLOC_FREE(pdp);
956 return NT_STATUS_OK;
959 static int setup_ver2_dfs_referral(const char *pathname,
960 char **ppdata,
961 struct junction_map *junction,
962 bool self_referral)
964 char* pdata = *ppdata;
966 smb_ucs2_t *uni_requestedpath = NULL;
967 int uni_reqpathoffset1,uni_reqpathoffset2;
968 int uni_curroffset;
969 int requestedpathlen=0;
970 int offset;
971 int reply_size = 0;
972 int i=0;
974 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
976 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
977 &uni_requestedpath, pathname);
978 if (uni_requestedpath == NULL || requestedpathlen == 0) {
979 return -1;
982 if (DEBUGLVL(10)) {
983 dump_data(0, (unsigned char *)uni_requestedpath,
984 requestedpathlen);
987 DEBUG(10,("ref count = %u\n",junction->referral_count));
989 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
990 VERSION2_REFERRAL_SIZE * junction->referral_count;
992 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
994 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
996 reply_size = REFERRAL_HEADER_SIZE +
997 VERSION2_REFERRAL_SIZE*junction->referral_count +
998 2 * requestedpathlen;
999 DEBUG(10,("reply_size: %u\n",reply_size));
1001 /* add up the unicode lengths of all the referral paths */
1002 for(i=0;i<junction->referral_count;i++) {
1003 DEBUG(10,("referral %u : %s\n",
1005 junction->referral_list[i].alternate_path));
1006 reply_size +=
1007 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1010 DEBUG(10,("reply_size = %u\n",reply_size));
1011 /* add the unexplained 0x16 bytes */
1012 reply_size += 0x16;
1014 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1015 if(pdata == NULL) {
1016 DEBUG(0,("Realloc failed!\n"));
1017 return -1;
1019 *ppdata = pdata;
1021 /* copy in the dfs requested paths.. required for offset calculations */
1022 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1023 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1025 /* create the header */
1026 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1027 2 byte null */
1028 /* number of referral in this pkt */
1029 SSVAL(pdata,2,junction->referral_count);
1030 if(self_referral) {
1031 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1032 } else {
1033 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1036 offset = 8;
1037 /* add the referral elements */
1038 for(i=0;i<junction->referral_count;i++) {
1039 struct referral* ref = &junction->referral_list[i];
1040 int unilen;
1042 SSVAL(pdata,offset,2); /* version 2 */
1043 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1044 if(self_referral) {
1045 SSVAL(pdata,offset+4,1);
1046 } else {
1047 SSVAL(pdata,offset+4,0);
1050 /* ref_flags :use path_consumed bytes? */
1051 SSVAL(pdata,offset+6,0);
1052 SIVAL(pdata,offset+8,ref->proximity);
1053 SIVAL(pdata,offset+12,ref->ttl);
1055 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1056 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1057 /* copy referred path into current offset */
1058 unilen = rpcstr_push(pdata+uni_curroffset,
1059 ref->alternate_path,
1060 reply_size - uni_curroffset,
1061 STR_UNICODE);
1063 SSVAL(pdata,offset+20,uni_curroffset-offset);
1065 uni_curroffset += unilen;
1066 offset += VERSION2_REFERRAL_SIZE;
1068 /* add in the unexplained 22 (0x16) bytes at the end */
1069 memset(pdata+uni_curroffset,'\0',0x16);
1070 return reply_size;
1073 static int setup_ver3_dfs_referral(const char *pathname,
1074 char **ppdata,
1075 struct junction_map *junction,
1076 bool self_referral)
1078 char *pdata = *ppdata;
1080 smb_ucs2_t *uni_reqpath = NULL;
1081 int uni_reqpathoffset1, uni_reqpathoffset2;
1082 int uni_curroffset;
1083 int reply_size = 0;
1085 int reqpathlen = 0;
1086 int offset,i=0;
1088 DEBUG(10,("setting up version3 referral\n"));
1090 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1091 if (uni_reqpath == NULL || reqpathlen == 0) {
1092 return -1;
1095 if (DEBUGLVL(10)) {
1096 dump_data(0, (unsigned char *)uni_reqpath,
1097 reqpathlen);
1100 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1101 VERSION3_REFERRAL_SIZE * junction->referral_count;
1102 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1103 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1105 for(i=0;i<junction->referral_count;i++) {
1106 DEBUG(10,("referral %u : %s\n",
1108 junction->referral_list[i].alternate_path));
1109 reply_size +=
1110 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1113 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1114 if(pdata == NULL) {
1115 DEBUG(0,("version3 referral setup:"
1116 "malloc failed for Realloc!\n"));
1117 return -1;
1119 *ppdata = pdata;
1121 /* create the header */
1122 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1123 2 byte null */
1124 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1125 if(self_referral) {
1126 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1127 } else {
1128 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1131 /* copy in the reqpaths */
1132 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1133 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1135 offset = 8;
1136 for(i=0;i<junction->referral_count;i++) {
1137 struct referral* ref = &(junction->referral_list[i]);
1138 int unilen;
1140 SSVAL(pdata,offset,3); /* version 3 */
1141 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1142 if(self_referral) {
1143 SSVAL(pdata,offset+4,1);
1144 } else {
1145 SSVAL(pdata,offset+4,0);
1148 /* ref_flags :use path_consumed bytes? */
1149 SSVAL(pdata,offset+6,0);
1150 SIVAL(pdata,offset+8,ref->ttl);
1152 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1153 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1154 /* copy referred path into current offset */
1155 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1156 reply_size - uni_curroffset,
1157 STR_UNICODE | STR_TERMINATE);
1158 SSVAL(pdata,offset+16,uni_curroffset-offset);
1159 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1160 memset(pdata+offset+18,'\0',16);
1162 uni_curroffset += unilen;
1163 offset += VERSION3_REFERRAL_SIZE;
1165 return reply_size;
1168 /******************************************************************
1169 Set up the DFS referral for the dfs pathname. This call returns
1170 the amount of the path covered by this server, and where the
1171 client should be redirected to. This is the meat of the
1172 TRANS2_GET_DFS_REFERRAL call.
1173 ******************************************************************/
1175 int setup_dfs_referral(connection_struct *orig_conn,
1176 const char *dfs_path,
1177 int max_referral_level,
1178 char **ppdata, NTSTATUS *pstatus)
1180 struct junction_map *junction = NULL;
1181 int consumedcnt = 0;
1182 bool self_referral = False;
1183 int reply_size = 0;
1184 char *pathnamep = NULL;
1185 char *local_dfs_path = NULL;
1186 TALLOC_CTX *ctx;
1188 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1189 *pstatus = NT_STATUS_NO_MEMORY;
1190 return -1;
1193 /* get the junction entry */
1194 if (!dfs_path) {
1195 talloc_destroy(ctx);
1196 *pstatus = NT_STATUS_NOT_FOUND;
1197 return -1;
1201 * Trim pathname sent by client so it begins with only one backslash.
1202 * Two backslashes confuse some dfs clients
1205 local_dfs_path = talloc_strdup(ctx,dfs_path);
1206 if (!local_dfs_path) {
1207 *pstatus = NT_STATUS_NO_MEMORY;
1208 talloc_destroy(ctx);
1209 return -1;
1211 pathnamep = local_dfs_path;
1212 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1213 IS_DIRECTORY_SEP(pathnamep[1])) {
1214 pathnamep++;
1217 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1218 if (!junction) {
1219 *pstatus = NT_STATUS_NO_MEMORY;
1220 talloc_destroy(ctx);
1221 return -1;
1224 /* The following call can change cwd. */
1225 *pstatus = get_referred_path(ctx, pathnamep, junction,
1226 &consumedcnt, &self_referral);
1227 if (!NT_STATUS_IS_OK(*pstatus)) {
1228 vfs_ChDir(orig_conn,orig_conn->connectpath);
1229 talloc_destroy(ctx);
1230 return -1;
1232 vfs_ChDir(orig_conn,orig_conn->connectpath);
1234 if (!self_referral) {
1235 pathnamep[consumedcnt] = '\0';
1237 if( DEBUGLVL( 3 ) ) {
1238 int i=0;
1239 dbgtext("setup_dfs_referral: Path %s to "
1240 "alternate path(s):",
1241 pathnamep);
1242 for(i=0;i<junction->referral_count;i++)
1243 dbgtext(" %s",
1244 junction->referral_list[i].alternate_path);
1245 dbgtext(".\n");
1249 /* create the referral depeding on version */
1250 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1252 if (max_referral_level < 2) {
1253 max_referral_level = 2;
1255 if (max_referral_level > 3) {
1256 max_referral_level = 3;
1259 switch(max_referral_level) {
1260 case 2:
1261 reply_size = setup_ver2_dfs_referral(pathnamep,
1262 ppdata, junction,
1263 self_referral);
1264 break;
1265 case 3:
1266 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1267 junction, self_referral);
1268 break;
1269 default:
1270 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1271 "version: %d\n",
1272 max_referral_level));
1273 talloc_destroy(ctx);
1274 *pstatus = NT_STATUS_INVALID_LEVEL;
1275 return -1;
1278 if (DEBUGLVL(10)) {
1279 DEBUGADD(0,("DFS Referral pdata:\n"));
1280 dump_data(0,(uint8 *)*ppdata,reply_size);
1283 talloc_destroy(ctx);
1284 *pstatus = NT_STATUS_OK;
1285 return reply_size;
1288 /**********************************************************************
1289 The following functions are called by the NETDFS RPC pipe functions
1290 **********************************************************************/
1292 /*********************************************************************
1293 Creates a junction structure from a DFS pathname
1294 **********************************************************************/
1296 bool create_junction(TALLOC_CTX *ctx,
1297 const char *dfs_path,
1298 struct junction_map *jucn)
1300 int snum;
1301 bool dummy;
1302 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1303 NTSTATUS status;
1305 if (!pdp) {
1306 return False;
1308 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1309 if (!NT_STATUS_IS_OK(status)) {
1310 return False;
1313 /* check if path is dfs : validate first token */
1314 if (!is_myname_or_ipaddr(pdp->hostname)) {
1315 DEBUG(4,("create_junction: Invalid hostname %s "
1316 "in dfs path %s\n",
1317 pdp->hostname, dfs_path));
1318 TALLOC_FREE(pdp);
1319 return False;
1322 /* Check for a non-DFS share */
1323 snum = lp_servicenumber(pdp->servicename);
1325 if(snum < 0 || !lp_msdfs_root(snum)) {
1326 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1327 pdp->servicename));
1328 TALLOC_FREE(pdp);
1329 return False;
1332 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1333 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1334 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1336 TALLOC_FREE(pdp);
1337 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1338 return False;
1340 return True;
1343 /**********************************************************************
1344 Forms a valid Unix pathname from the junction
1345 **********************************************************************/
1347 static bool junction_to_local_path(const struct junction_map *jucn,
1348 char **pp_path_out,
1349 connection_struct **conn_out,
1350 char **oldpath)
1352 int snum;
1353 NTSTATUS status;
1355 snum = lp_servicenumber(jucn->service_name);
1356 if(snum < 0) {
1357 return False;
1359 status = create_conn_struct(talloc_tos(), conn_out, snum,
1360 lp_pathname(snum), NULL, oldpath);
1361 if (!NT_STATUS_IS_OK(status)) {
1362 return False;
1365 *pp_path_out = talloc_asprintf(*conn_out,
1366 "%s/%s",
1367 lp_pathname(snum),
1368 jucn->volume_name);
1369 if (!*pp_path_out) {
1370 vfs_ChDir(*conn_out, *oldpath);
1371 conn_free(*conn_out);
1372 return False;
1374 return True;
1377 bool create_msdfs_link(const struct junction_map *jucn)
1379 char *path = NULL;
1380 char *cwd;
1381 char *msdfs_link = NULL;
1382 connection_struct *conn;
1383 int i=0;
1384 bool insert_comma = False;
1385 bool ret = False;
1387 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1388 return False;
1391 /* Form the msdfs_link contents */
1392 msdfs_link = talloc_strdup(conn, "msdfs:");
1393 if (!msdfs_link) {
1394 goto out;
1396 for(i=0; i<jucn->referral_count; i++) {
1397 char *refpath = jucn->referral_list[i].alternate_path;
1399 /* Alternate paths always use Windows separators. */
1400 trim_char(refpath, '\\', '\\');
1401 if(*refpath == '\0') {
1402 if (i == 0) {
1403 insert_comma = False;
1405 continue;
1407 if (i > 0 && insert_comma) {
1408 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1409 ",%s",
1410 refpath);
1411 } else {
1412 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1413 "%s",
1414 refpath);
1417 if (!msdfs_link) {
1418 goto out;
1420 if (!insert_comma) {
1421 insert_comma = True;
1425 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1426 path, msdfs_link));
1428 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1429 if (errno == EEXIST) {
1430 struct smb_filename *smb_fname = NULL;
1431 NTSTATUS status;
1433 status = create_synthetic_smb_fname(talloc_tos(), path,
1434 NULL, NULL,
1435 &smb_fname);
1436 if (!NT_STATUS_IS_OK(status)) {
1437 errno = map_errno_from_nt_status(status);
1438 goto out;
1441 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1442 TALLOC_FREE(smb_fname);
1443 goto out;
1445 TALLOC_FREE(smb_fname);
1447 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1448 DEBUG(1,("create_msdfs_link: symlink failed "
1449 "%s -> %s\nError: %s\n",
1450 path, msdfs_link, strerror(errno)));
1451 goto out;
1455 ret = True;
1457 out:
1458 vfs_ChDir(conn, cwd);
1459 conn_free(conn);
1460 return ret;
1463 bool remove_msdfs_link(const struct junction_map *jucn)
1465 char *path = NULL;
1466 char *cwd;
1467 connection_struct *conn;
1468 bool ret = False;
1469 struct smb_filename *smb_fname = NULL;
1470 NTSTATUS status;
1472 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1473 return false;
1476 status = create_synthetic_smb_fname(talloc_tos(), path,
1477 NULL, NULL,
1478 &smb_fname);
1479 if (!NT_STATUS_IS_OK(status)) {
1480 errno = map_errno_from_nt_status(status);
1481 return false;
1484 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1485 ret = True;
1488 TALLOC_FREE(smb_fname);
1489 vfs_ChDir(conn, cwd);
1490 conn_free(conn);
1491 return ret;
1494 /*********************************************************************
1495 Return the number of DFS links at the root of this share.
1496 *********************************************************************/
1498 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1500 size_t cnt = 0;
1501 SMB_STRUCT_DIR *dirp = NULL;
1502 const char *dname = NULL;
1503 char *talloced = NULL;
1504 const char *connect_path = lp_pathname(snum);
1505 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1506 connection_struct *conn;
1507 NTSTATUS status;
1508 char *cwd;
1510 if(*connect_path == '\0') {
1511 return 0;
1515 * Fake up a connection struct for the VFS layer.
1518 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1519 NULL, &cwd);
1520 if (!NT_STATUS_IS_OK(status)) {
1521 DEBUG(3, ("create_conn_struct failed: %s\n",
1522 nt_errstr(status)));
1523 return 0;
1526 /* Count a link for the msdfs root - convention */
1527 cnt = 1;
1529 /* No more links if this is an msdfs proxy. */
1530 if (*msdfs_proxy != '\0') {
1531 goto out;
1534 /* Now enumerate all dfs links */
1535 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1536 if(!dirp) {
1537 goto out;
1540 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1541 != NULL) {
1542 if (is_msdfs_link(conn,
1543 dname,
1544 NULL)) {
1545 cnt++;
1547 TALLOC_FREE(talloced);
1550 SMB_VFS_CLOSEDIR(conn,dirp);
1552 out:
1553 vfs_ChDir(conn, cwd);
1554 conn_free(conn);
1555 return cnt;
1558 /*********************************************************************
1559 *********************************************************************/
1561 static int form_junctions(TALLOC_CTX *ctx,
1562 int snum,
1563 struct junction_map *jucn,
1564 size_t jn_remain)
1566 size_t cnt = 0;
1567 SMB_STRUCT_DIR *dirp = NULL;
1568 const char *dname = NULL;
1569 char *talloced = NULL;
1570 const char *connect_path = lp_pathname(snum);
1571 char *service_name = lp_servicename(snum);
1572 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1573 connection_struct *conn;
1574 struct referral *ref = NULL;
1575 char *cwd;
1576 NTSTATUS status;
1578 if (jn_remain == 0) {
1579 return 0;
1582 if(*connect_path == '\0') {
1583 return 0;
1587 * Fake up a connection struct for the VFS layer.
1590 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1591 &cwd);
1592 if (!NT_STATUS_IS_OK(status)) {
1593 DEBUG(3, ("create_conn_struct failed: %s\n",
1594 nt_errstr(status)));
1595 return 0;
1598 /* form a junction for the msdfs root - convention
1599 DO NOT REMOVE THIS: NT clients will not work with us
1600 if this is not present
1602 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1603 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1604 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1605 goto out;
1607 jucn[cnt].comment = "";
1608 jucn[cnt].referral_count = 1;
1610 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1611 if (jucn[cnt].referral_list == NULL) {
1612 goto out;
1615 ref->proximity = 0;
1616 ref->ttl = REFERRAL_TTL;
1617 if (*msdfs_proxy != '\0') {
1618 ref->alternate_path = talloc_strdup(ctx,
1619 msdfs_proxy);
1620 } else {
1621 ref->alternate_path = talloc_asprintf(ctx,
1622 "\\\\%s\\%s",
1623 get_local_machine_name(),
1624 service_name);
1627 if (!ref->alternate_path) {
1628 goto out;
1630 cnt++;
1632 /* Don't enumerate if we're an msdfs proxy. */
1633 if (*msdfs_proxy != '\0') {
1634 goto out;
1637 /* Now enumerate all dfs links */
1638 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1639 if(!dirp) {
1640 goto out;
1643 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1644 != NULL) {
1645 char *link_target = NULL;
1646 if (cnt >= jn_remain) {
1647 DEBUG(2, ("form_junctions: ran out of MSDFS "
1648 "junction slots"));
1649 TALLOC_FREE(talloced);
1650 goto out;
1652 if (is_msdfs_link_internal(ctx,
1653 conn,
1654 dname, &link_target,
1655 NULL)) {
1656 if (parse_msdfs_symlink(ctx,
1657 link_target,
1658 &jucn[cnt].referral_list,
1659 &jucn[cnt].referral_count)) {
1661 jucn[cnt].service_name = talloc_strdup(ctx,
1662 service_name);
1663 jucn[cnt].volume_name = talloc_strdup(ctx,
1664 dname);
1665 if (!jucn[cnt].service_name ||
1666 !jucn[cnt].volume_name) {
1667 TALLOC_FREE(talloced);
1668 goto out;
1670 jucn[cnt].comment = "";
1671 cnt++;
1673 TALLOC_FREE(link_target);
1675 TALLOC_FREE(talloced);
1678 out:
1680 if (dirp) {
1681 SMB_VFS_CLOSEDIR(conn,dirp);
1684 vfs_ChDir(conn, cwd);
1685 conn_free(conn);
1686 return cnt;
1689 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1691 struct junction_map *jn = NULL;
1692 int i=0;
1693 size_t jn_count = 0;
1694 int sharecount = 0;
1696 *p_num_jn = 0;
1697 if(!lp_host_msdfs()) {
1698 return NULL;
1701 /* Ensure all the usershares are loaded. */
1702 become_root();
1703 load_registry_shares();
1704 sharecount = load_usershare_shares();
1705 unbecome_root();
1707 for(i=0;i < sharecount;i++) {
1708 if(lp_msdfs_root(i)) {
1709 jn_count += count_dfs_links(ctx, i);
1712 if (jn_count == 0) {
1713 return NULL;
1715 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1716 if (!jn) {
1717 return NULL;
1719 for(i=0; i < sharecount; i++) {
1720 if (*p_num_jn >= jn_count) {
1721 break;
1723 if(lp_msdfs_root(i)) {
1724 *p_num_jn += form_junctions(ctx, i,
1725 &jn[*p_num_jn],
1726 jn_count - *p_num_jn);
1729 return jn;
1732 /******************************************************************************
1733 Core function to resolve a dfs pathname possibly containing a wildcard. If
1734 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1735 detected during dfs resolution.
1736 ******************************************************************************/
1738 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1739 connection_struct *conn,
1740 bool dfs_pathnames,
1741 const char *name_in,
1742 bool allow_wcards,
1743 char **pp_name_out,
1744 bool *ppath_contains_wcard)
1746 bool path_contains_wcard;
1747 NTSTATUS status = NT_STATUS_OK;
1749 if (dfs_pathnames) {
1750 status = dfs_redirect(ctx,
1751 conn,
1752 name_in,
1753 allow_wcards,
1754 pp_name_out,
1755 &path_contains_wcard);
1757 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1758 *ppath_contains_wcard = path_contains_wcard;
1760 } else {
1762 * Cheat and just return a copy of the in ptr.
1763 * Once srvstr_get_path() uses talloc it'll
1764 * be a talloced ptr anyway.
1766 *pp_name_out = CONST_DISCARD(char *,name_in);
1768 return status;