Fix bug #7781 (Samba transforms "ShareName" to lowercase when adding new share via...
[Samba.git] / source3 / smbd / msdfs.c
blob718d0def7214514c4660ef825f409745deb9bb0f
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 char *pathname_local;
54 char *p,*temp;
55 char *servicename;
56 char *eos_ptr;
57 NTSTATUS status = NT_STATUS_OK;
58 char sepchar;
60 ZERO_STRUCTP(pdp);
63 * This is the only talloc we should need to do
64 * on the struct dfs_path. All the pointers inside
65 * it should point to offsets within this string.
68 pathname_local = talloc_strdup(pdp, pathname);
69 if (!pathname_local) {
70 return NT_STATUS_NO_MEMORY;
72 /* Get a pointer to the terminating '\0' */
73 eos_ptr = &pathname_local[strlen(pathname_local)];
74 p = temp = pathname_local;
76 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
78 sepchar = pdp->posix_path ? '/' : '\\';
80 if (*pathname != sepchar) {
81 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
82 pathname, sepchar ));
84 * Possibly client sent a local path by mistake.
85 * Try and convert to a local path.
88 pdp->hostname = eos_ptr; /* "" */
89 pdp->servicename = eos_ptr; /* "" */
91 /* We've got no info about separators. */
92 pdp->posix_path = lp_posix_pathnames();
93 p = temp;
94 DEBUG(10,("parse_dfs_path: trying to convert %s to a "
95 "local path\n",
96 temp));
97 goto local_path;
101 * Safe to use on talloc'ed string as it only shrinks.
102 * It also doesn't affect the eos_ptr.
104 trim_char(temp,sepchar,sepchar);
106 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
107 temp, sepchar));
109 /* Now tokenize. */
110 /* Parse out hostname. */
111 p = strchr_m(temp,sepchar);
112 if(p == NULL) {
113 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
114 temp));
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp->hostname = eos_ptr; /* "" */
121 pdp->servicename = eos_ptr; /* "" */
123 p = temp;
124 DEBUG(10,("parse_dfs_path: trying to convert %s "
125 "to a local path\n",
126 temp));
127 goto local_path;
129 *p = '\0';
130 pdp->hostname = temp;
132 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
134 /* Parse out servicename. */
135 servicename = p+1;
136 p = strchr_m(servicename,sepchar);
137 if (p) {
138 *p = '\0';
141 /* Is this really our servicename ? */
142 if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
143 || (strequal(servicename, HOMES_NAME)
144 && strequal(lp_servicename(SNUM(conn)),
145 get_current_username()) )) ) {
146 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
147 servicename));
150 * Possibly client sent a local path by mistake.
151 * Try and convert to a local path.
154 pdp->hostname = eos_ptr; /* "" */
155 pdp->servicename = eos_ptr; /* "" */
157 /* Repair the path - replace the sepchar's
158 we nulled out */
159 servicename--;
160 *servicename = sepchar;
161 if (p) {
162 *p = sepchar;
165 p = temp;
166 DEBUG(10,("parse_dfs_path: trying to convert %s "
167 "to a local path\n",
168 temp));
169 goto local_path;
172 pdp->servicename = servicename;
174 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
176 if(p == NULL) {
177 /* Client sent self referral \server\share. */
178 pdp->reqpath = eos_ptr; /* "" */
179 return NT_STATUS_OK;
182 p++;
184 local_path:
186 *ppath_contains_wcard = False;
188 pdp->reqpath = p;
190 /* Rest is reqpath. */
191 if (pdp->posix_path) {
192 status = check_path_syntax_posix(pdp->reqpath);
193 } else {
194 if (allow_wcards) {
195 status = check_path_syntax_wcard(pdp->reqpath,
196 ppath_contains_wcard);
197 } else {
198 status = check_path_syntax(pdp->reqpath);
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
204 p, nt_errstr(status) ));
205 return status;
208 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
209 return NT_STATUS_OK;
212 /********************************************************
213 Fake up a connection struct for the VFS layer.
214 Note this CHANGES CWD !!!! JRA.
215 *********************************************************/
217 NTSTATUS create_conn_struct(TALLOC_CTX *ctx,
218 connection_struct **pconn,
219 int snum,
220 const char *path,
221 struct auth_serversupplied_info *server_info,
222 char **poldcwd)
224 connection_struct *conn;
225 char *connpath;
226 char *oldcwd;
228 conn = TALLOC_ZERO_P(ctx, connection_struct);
229 if (conn == NULL) {
230 return NT_STATUS_NO_MEMORY;
233 connpath = talloc_strdup(conn, path);
234 if (!connpath) {
235 TALLOC_FREE(conn);
236 return NT_STATUS_NO_MEMORY;
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;
247 /* needed for smbd_vfs_init() */
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;
255 conn->params->service = snum;
257 if (server_info != NULL) {
258 conn->server_info = copy_serverinfo(conn, server_info);
259 if (conn->server_info == NULL) {
260 DEBUG(0, ("copy_serverinfo failed\n"));
261 TALLOC_FREE(conn);
262 return NT_STATUS_NO_MEMORY;
266 set_conn_connectpath(conn, connpath);
268 if (!smbd_vfs_init(conn)) {
269 NTSTATUS status = map_nt_error_from_unix(errno);
270 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
271 conn_free(conn);
272 return status;
275 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
278 * Windows seems to insist on doing trans2getdfsreferral() calls on
279 * the IPC$ share as the anonymous user. If we try to chdir as that
280 * user we will fail.... WTF ? JRA.
283 oldcwd = vfs_GetWd(ctx, conn);
284 if (oldcwd == NULL) {
285 NTSTATUS status = map_nt_error_from_unix(errno);
286 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
287 conn_free(conn);
288 return status;
291 if (vfs_ChDir(conn,conn->connectpath) != 0) {
292 NTSTATUS status = map_nt_error_from_unix(errno);
293 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
294 "Error was %s\n",
295 conn->connectpath, strerror(errno) ));
296 conn_free(conn);
297 return status;
300 *pconn = conn;
301 *poldcwd = oldcwd;
303 return NT_STATUS_OK;
306 /**********************************************************************
307 Parse the contents of a symlink to verify if it is an msdfs referral
308 A valid referral is of the form:
310 msdfs:server1\share1,server2\share2
311 msdfs:server1\share1\pathname,server2\share2\pathname
312 msdfs:server1/share1,server2/share2
313 msdfs:server1/share1/pathname,server2/share2/pathname.
315 Note that the alternate paths returned here must be of the canonicalized
316 form:
318 \server\share or
319 \server\share\path\to\file,
321 even in posix path mode. This is because we have no knowledge if the
322 server we're referring to understands posix paths.
323 **********************************************************************/
325 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
326 const char *target,
327 struct referral **preflist,
328 int *refcount)
330 char *temp = NULL;
331 char *prot;
332 char **alt_path = NULL;
333 int count = 0, i;
334 struct referral *reflist;
335 char *saveptr;
337 temp = talloc_strdup(ctx, target);
338 if (!temp) {
339 return False;
341 prot = strtok_r(temp, ":", &saveptr);
342 if (!prot) {
343 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
344 return False;
347 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
348 if (!alt_path) {
349 return False;
352 /* parse out the alternate paths */
353 while((count<MAX_REFERRAL_COUNT) &&
354 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
355 count++;
358 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
360 if (count) {
361 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
362 struct referral, count);
363 if(reflist == NULL) {
364 TALLOC_FREE(alt_path);
365 return False;
367 } else {
368 reflist = *preflist = NULL;
371 for(i=0;i<count;i++) {
372 char *p;
374 /* Canonicalize link target.
375 * Replace all /'s in the path by a \ */
376 string_replace(alt_path[i], '/', '\\');
378 /* Remove leading '\\'s */
379 p = alt_path[i];
380 while (*p && (*p == '\\')) {
381 p++;
384 reflist[i].alternate_path = talloc_asprintf(ctx,
385 "\\%s",
387 if (!reflist[i].alternate_path) {
388 return False;
391 reflist[i].proximity = 0;
392 reflist[i].ttl = REFERRAL_TTL;
393 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
394 reflist[i].alternate_path));
397 *refcount = count;
399 TALLOC_FREE(alt_path);
400 return True;
403 /**********************************************************************
404 Returns true if the unix path is a valid msdfs symlink and also
405 returns the target string from inside the link.
406 **********************************************************************/
408 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
409 connection_struct *conn,
410 const char *path,
411 char **pp_link_target,
412 SMB_STRUCT_STAT *sbufp)
414 int referral_len = 0;
415 #if defined(HAVE_BROKEN_READLINK)
416 char link_target_buf[PATH_MAX];
417 #else
418 char link_target_buf[7];
419 #endif
420 size_t bufsize = 0;
421 char *link_target = NULL;
422 struct smb_filename smb_fname;
424 if (pp_link_target) {
425 bufsize = 1024;
426 link_target = TALLOC_ARRAY(ctx, char, bufsize);
427 if (!link_target) {
428 return False;
430 *pp_link_target = link_target;
431 } else {
432 bufsize = sizeof(link_target_buf);
433 link_target = link_target_buf;
436 ZERO_STRUCT(smb_fname);
437 smb_fname.base_name = discard_const_p(char, path);
439 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
440 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
441 path));
442 goto err;
444 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
445 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
446 path));
447 goto err;
449 if (sbufp != NULL) {
450 *sbufp = smb_fname.st;
453 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
454 if (referral_len == -1) {
455 DEBUG(0,("is_msdfs_link_read_target: Error reading "
456 "msdfs link %s: %s\n",
457 path, strerror(errno)));
458 goto err;
460 link_target[referral_len] = '\0';
462 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
463 link_target));
465 if (!strnequal(link_target, "msdfs:", 6)) {
466 goto err;
468 return True;
470 err:
472 if (link_target != link_target_buf) {
473 TALLOC_FREE(link_target);
475 return False;
478 /**********************************************************************
479 Returns true if the unix path is a valid msdfs symlink.
480 **********************************************************************/
482 bool is_msdfs_link(connection_struct *conn,
483 const char *path,
484 SMB_STRUCT_STAT *sbufp)
486 return is_msdfs_link_internal(talloc_tos(),
487 conn,
488 path,
489 NULL,
490 sbufp);
493 /*****************************************************************
494 Used by other functions to decide if a dfs path is remote,
495 and to get the list of referred locations for that remote path.
497 search_flag: For findfirsts, dfs links themselves are not
498 redirected, but paths beyond the links are. For normal smb calls,
499 even dfs links need to be redirected.
501 consumedcntp: how much of the dfs path is being redirected. the client
502 should try the remaining path on the redirected server.
504 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
505 link redirect are in targetpath.
506 *****************************************************************/
508 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
509 connection_struct *conn,
510 const char *dfspath, /* Incoming complete dfs path */
511 const struct dfs_path *pdp, /* Parsed out
512 server+share+extrapath. */
513 bool search_flag, /* Called from a findfirst ? */
514 int *consumedcntp,
515 char **pp_targetpath)
517 char *p = NULL;
518 char *q = NULL;
519 NTSTATUS status;
520 struct smb_filename *smb_fname = NULL;
521 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
522 components). */
524 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
525 conn->connectpath, pdp->reqpath));
528 * Note the unix path conversion here we're doing we
529 * throw away. We're looking for a symlink for a dfs
530 * resolution, if we don't find it we'll do another
531 * unix_convert later in the codepath.
534 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
535 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
537 if (!NT_STATUS_IS_OK(status)) {
538 if (!NT_STATUS_EQUAL(status,
539 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
540 return status;
542 if (smb_fname == NULL || smb_fname->base_name == NULL) {
543 return status;
547 /* Optimization - check if we can redirect the whole path. */
549 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
550 pp_targetpath, NULL)) {
551 if (search_flag) {
552 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
553 "for dfs link %s.\n", dfspath));
554 status = NT_STATUS_OK;
555 goto out;
558 DEBUG(6,("dfs_path_lookup: %s resolves to a "
559 "valid dfs link %s.\n", dfspath,
560 pp_targetpath ? *pp_targetpath : ""));
562 if (consumedcntp) {
563 *consumedcntp = strlen(dfspath);
565 status = NT_STATUS_PATH_NOT_COVERED;
566 goto out;
569 /* Prepare to test only for '/' components in the given path,
570 * so if a Windows path replace all '\\' characters with '/'.
571 * For a POSIX DFS path we know all separators are already '/'. */
573 canon_dfspath = talloc_strdup(ctx, dfspath);
574 if (!canon_dfspath) {
575 status = NT_STATUS_NO_MEMORY;
576 goto out;
578 if (!pdp->posix_path) {
579 string_replace(canon_dfspath, '\\', '/');
583 * localpath comes out of unix_convert, so it has
584 * no trailing backslash. Make sure that canon_dfspath hasn't either.
585 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
588 trim_char(canon_dfspath,0,'/');
591 * Redirect if any component in the path is a link.
592 * We do this by walking backwards through the
593 * local path, chopping off the last component
594 * in both the local path and the canonicalized
595 * DFS path. If we hit a DFS link then we're done.
598 p = strrchr_m(smb_fname->base_name, '/');
599 if (consumedcntp) {
600 q = strrchr_m(canon_dfspath, '/');
603 while (p) {
604 *p = '\0';
605 if (q) {
606 *q = '\0';
609 if (is_msdfs_link_internal(ctx, conn,
610 smb_fname->base_name, pp_targetpath,
611 NULL)) {
612 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
613 "parent %s is dfs link\n", dfspath,
614 smb_fname_str_dbg(smb_fname)));
616 if (consumedcntp) {
617 *consumedcntp = strlen(canon_dfspath);
618 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
619 "(%d)\n",
620 canon_dfspath,
621 *consumedcntp));
624 status = NT_STATUS_PATH_NOT_COVERED;
625 goto out;
628 /* Step back on the filesystem. */
629 p = strrchr_m(smb_fname->base_name, '/');
631 if (consumedcntp) {
632 /* And in the canonicalized dfs path. */
633 q = strrchr_m(canon_dfspath, '/');
637 status = NT_STATUS_OK;
638 out:
639 TALLOC_FREE(smb_fname);
640 return status;
643 /*****************************************************************
644 Decides if a dfs pathname should be redirected or not.
645 If not, the pathname is converted to a tcon-relative local unix path
647 search_wcard_flag: this flag performs 2 functions both related
648 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
649 for details.
651 This function can return NT_STATUS_OK, meaning use the returned path as-is
652 (mapped into a local path).
653 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
654 any other NT_STATUS error which is a genuine error to be
655 returned to the client.
656 *****************************************************************/
658 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
659 connection_struct *conn,
660 const char *path_in,
661 bool search_wcard_flag,
662 char **pp_path_out,
663 bool *ppath_contains_wcard)
665 NTSTATUS status;
666 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
668 if (!pdp) {
669 return NT_STATUS_NO_MEMORY;
672 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
673 ppath_contains_wcard);
674 if (!NT_STATUS_IS_OK(status)) {
675 TALLOC_FREE(pdp);
676 return status;
679 if (pdp->reqpath[0] == '\0') {
680 TALLOC_FREE(pdp);
681 *pp_path_out = talloc_strdup(ctx, "");
682 if (!*pp_path_out) {
683 return NT_STATUS_NO_MEMORY;
685 DEBUG(5,("dfs_redirect: self-referral.\n"));
686 return NT_STATUS_OK;
689 /* If dfs pathname for a non-dfs share, convert to tcon-relative
690 path and return OK */
692 if (!lp_msdfs_root(SNUM(conn))) {
693 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
694 TALLOC_FREE(pdp);
695 if (!*pp_path_out) {
696 return NT_STATUS_NO_MEMORY;
698 return NT_STATUS_OK;
701 /* If it looked like a local path (zero hostname/servicename)
702 * just treat as a tcon-relative path. */
704 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
705 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
706 TALLOC_FREE(pdp);
707 if (!*pp_path_out) {
708 return NT_STATUS_NO_MEMORY;
710 return NT_STATUS_OK;
713 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
714 || (strequal(pdp->servicename, HOMES_NAME)
715 && strequal(lp_servicename(SNUM(conn)),
716 conn->server_info->sanitized_username) )) ) {
718 /* The given sharename doesn't match this connection. */
719 TALLOC_FREE(pdp);
721 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
724 status = dfs_path_lookup(ctx, conn, path_in, pdp,
725 search_wcard_flag, NULL, NULL);
726 if (!NT_STATUS_IS_OK(status)) {
727 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
728 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
729 } else {
730 DEBUG(10,("dfs_redirect: dfs_path_lookup "
731 "failed for %s with %s\n",
732 path_in, nt_errstr(status) ));
734 return status;
737 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
739 /* Form non-dfs tcon-relative path */
740 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
741 TALLOC_FREE(pdp);
742 if (!*pp_path_out) {
743 return NT_STATUS_NO_MEMORY;
746 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
747 path_in,
748 *pp_path_out));
750 return NT_STATUS_OK;
753 /**********************************************************************
754 Return a self referral.
755 **********************************************************************/
757 static NTSTATUS self_ref(TALLOC_CTX *ctx,
758 const char *dfs_path,
759 struct junction_map *jucn,
760 int *consumedcntp,
761 bool *self_referralp)
763 struct referral *ref;
765 *self_referralp = True;
767 jucn->referral_count = 1;
768 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
769 return NT_STATUS_NO_MEMORY;
772 ref->alternate_path = talloc_strdup(ctx, dfs_path);
773 if (!ref->alternate_path) {
774 return NT_STATUS_NO_MEMORY;
776 ref->proximity = 0;
777 ref->ttl = REFERRAL_TTL;
778 jucn->referral_list = ref;
779 *consumedcntp = strlen(dfs_path);
780 return NT_STATUS_OK;
783 /**********************************************************************
784 Gets valid referrals for a dfs path and fills up the
785 junction_map structure.
786 **********************************************************************/
788 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
789 struct auth_serversupplied_info *server_info,
790 const char *dfs_path,
791 struct junction_map *jucn,
792 int *consumedcntp,
793 bool *self_referralp)
795 struct connection_struct *conn;
796 char *targetpath = NULL;
797 int snum;
798 NTSTATUS status = NT_STATUS_NOT_FOUND;
799 bool dummy;
800 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
801 char *oldpath;
803 if (!pdp) {
804 return NT_STATUS_NO_MEMORY;
807 *self_referralp = False;
809 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
810 if (!NT_STATUS_IS_OK(status)) {
811 return status;
814 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
815 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
816 if (!jucn->service_name || !jucn->volume_name) {
817 TALLOC_FREE(pdp);
818 return NT_STATUS_NO_MEMORY;
821 /* Verify the share is a dfs root */
822 snum = lp_servicenumber(jucn->service_name);
823 if(snum < 0) {
824 fstring service_name;
825 if ((snum = find_service(jucn->service_name, service_name)) < 0) {
826 return NT_STATUS_NOT_FOUND;
828 TALLOC_FREE(jucn->service_name);
829 jucn->service_name = talloc_strdup(ctx, service_name);
830 if (!jucn->service_name) {
831 TALLOC_FREE(pdp);
832 return NT_STATUS_NO_MEMORY;
836 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
837 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
838 "a dfs root.\n",
839 pdp->servicename, dfs_path));
840 TALLOC_FREE(pdp);
841 return NT_STATUS_NOT_FOUND;
845 * Self referrals are tested with a anonymous IPC connection and
846 * a GET_DFS_REFERRAL call to \\server\share. (which means
847 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
848 * into the directory and will fail if it cannot (as the anonymous
849 * user). Cope with this.
852 if (pdp->reqpath[0] == '\0') {
853 char *tmp;
854 struct referral *ref;
856 if (*lp_msdfs_proxy(snum) == '\0') {
857 TALLOC_FREE(pdp);
858 return self_ref(ctx,
859 dfs_path,
860 jucn,
861 consumedcntp,
862 self_referralp);
866 * It's an msdfs proxy share. Redirect to
867 * the configured target share.
870 jucn->referral_count = 1;
871 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
872 TALLOC_FREE(pdp);
873 return NT_STATUS_NO_MEMORY;
876 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
877 TALLOC_FREE(pdp);
878 return NT_STATUS_NO_MEMORY;
881 trim_string(tmp, "\\", 0);
883 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
884 TALLOC_FREE(tmp);
886 if (!ref->alternate_path) {
887 TALLOC_FREE(pdp);
888 return NT_STATUS_NO_MEMORY;
891 if (pdp->reqpath[0] != '\0') {
892 ref->alternate_path = talloc_asprintf_append(
893 ref->alternate_path,
894 "%s",
895 pdp->reqpath);
896 if (!ref->alternate_path) {
897 TALLOC_FREE(pdp);
898 return NT_STATUS_NO_MEMORY;
901 ref->proximity = 0;
902 ref->ttl = REFERRAL_TTL;
903 jucn->referral_list = ref;
904 *consumedcntp = strlen(dfs_path);
905 TALLOC_FREE(pdp);
906 return NT_STATUS_OK;
909 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
910 server_info, &oldpath);
911 if (!NT_STATUS_IS_OK(status)) {
912 TALLOC_FREE(pdp);
913 return status;
916 /* If this is a DFS path dfs_lookup should return
917 * NT_STATUS_PATH_NOT_COVERED. */
919 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
920 False, consumedcntp, &targetpath);
922 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
923 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
924 dfs_path));
925 vfs_ChDir(conn, oldpath);
926 conn_free(conn);
927 TALLOC_FREE(pdp);
928 return status;
931 /* We know this is a valid dfs link. Parse the targetpath. */
932 if (!parse_msdfs_symlink(ctx, targetpath,
933 &jucn->referral_list,
934 &jucn->referral_count)) {
935 DEBUG(3,("get_referred_path: failed to parse symlink "
936 "target %s\n", targetpath ));
937 vfs_ChDir(conn, oldpath);
938 conn_free(conn);
939 TALLOC_FREE(pdp);
940 return NT_STATUS_NOT_FOUND;
943 vfs_ChDir(conn, oldpath);
944 conn_free(conn);
945 TALLOC_FREE(pdp);
946 return NT_STATUS_OK;
949 static int setup_ver2_dfs_referral(const char *pathname,
950 char **ppdata,
951 struct junction_map *junction,
952 bool self_referral)
954 char* pdata = *ppdata;
956 smb_ucs2_t *uni_requestedpath = NULL;
957 int uni_reqpathoffset1,uni_reqpathoffset2;
958 int uni_curroffset;
959 int requestedpathlen=0;
960 int offset;
961 int reply_size = 0;
962 int i=0;
964 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
966 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
967 &uni_requestedpath, pathname);
968 if (uni_requestedpath == NULL || requestedpathlen == 0) {
969 return -1;
972 if (DEBUGLVL(10)) {
973 dump_data(0, (unsigned char *)uni_requestedpath,
974 requestedpathlen);
977 DEBUG(10,("ref count = %u\n",junction->referral_count));
979 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
980 VERSION2_REFERRAL_SIZE * junction->referral_count;
982 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
984 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
986 reply_size = REFERRAL_HEADER_SIZE +
987 VERSION2_REFERRAL_SIZE*junction->referral_count +
988 2 * requestedpathlen;
989 DEBUG(10,("reply_size: %u\n",reply_size));
991 /* add up the unicode lengths of all the referral paths */
992 for(i=0;i<junction->referral_count;i++) {
993 DEBUG(10,("referral %u : %s\n",
995 junction->referral_list[i].alternate_path));
996 reply_size +=
997 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1000 DEBUG(10,("reply_size = %u\n",reply_size));
1001 /* add the unexplained 0x16 bytes */
1002 reply_size += 0x16;
1004 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1005 if(pdata == NULL) {
1006 DEBUG(0,("Realloc failed!\n"));
1007 return -1;
1009 *ppdata = pdata;
1011 /* copy in the dfs requested paths.. required for offset calculations */
1012 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1013 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1015 /* create the header */
1016 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1017 2 byte null */
1018 /* number of referral in this pkt */
1019 SSVAL(pdata,2,junction->referral_count);
1020 if(self_referral) {
1021 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1022 } else {
1023 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1026 offset = 8;
1027 /* add the referral elements */
1028 for(i=0;i<junction->referral_count;i++) {
1029 struct referral* ref = &junction->referral_list[i];
1030 int unilen;
1032 SSVAL(pdata,offset,2); /* version 2 */
1033 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1034 if(self_referral) {
1035 SSVAL(pdata,offset+4,1);
1036 } else {
1037 SSVAL(pdata,offset+4,0);
1040 /* ref_flags :use path_consumed bytes? */
1041 SSVAL(pdata,offset+6,0);
1042 SIVAL(pdata,offset+8,ref->proximity);
1043 SIVAL(pdata,offset+12,ref->ttl);
1045 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1046 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1047 /* copy referred path into current offset */
1048 unilen = rpcstr_push(pdata+uni_curroffset,
1049 ref->alternate_path,
1050 reply_size - uni_curroffset,
1051 STR_UNICODE);
1053 SSVAL(pdata,offset+20,uni_curroffset-offset);
1055 uni_curroffset += unilen;
1056 offset += VERSION2_REFERRAL_SIZE;
1058 /* add in the unexplained 22 (0x16) bytes at the end */
1059 memset(pdata+uni_curroffset,'\0',0x16);
1060 return reply_size;
1063 static int setup_ver3_dfs_referral(const char *pathname,
1064 char **ppdata,
1065 struct junction_map *junction,
1066 bool self_referral)
1068 char *pdata = *ppdata;
1070 smb_ucs2_t *uni_reqpath = NULL;
1071 int uni_reqpathoffset1, uni_reqpathoffset2;
1072 int uni_curroffset;
1073 int reply_size = 0;
1075 int reqpathlen = 0;
1076 int offset,i=0;
1078 DEBUG(10,("setting up version3 referral\n"));
1080 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1081 if (uni_reqpath == NULL || reqpathlen == 0) {
1082 return -1;
1085 if (DEBUGLVL(10)) {
1086 dump_data(0, (unsigned char *)uni_reqpath,
1087 reqpathlen);
1090 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1091 VERSION3_REFERRAL_SIZE * junction->referral_count;
1092 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1093 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1095 for(i=0;i<junction->referral_count;i++) {
1096 DEBUG(10,("referral %u : %s\n",
1098 junction->referral_list[i].alternate_path));
1099 reply_size +=
1100 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1103 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1104 if(pdata == NULL) {
1105 DEBUG(0,("version3 referral setup:"
1106 "malloc failed for Realloc!\n"));
1107 return -1;
1109 *ppdata = pdata;
1111 /* create the header */
1112 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1113 2 byte null */
1114 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1115 if(self_referral) {
1116 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1117 } else {
1118 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1121 /* copy in the reqpaths */
1122 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1123 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1125 offset = 8;
1126 for(i=0;i<junction->referral_count;i++) {
1127 struct referral* ref = &(junction->referral_list[i]);
1128 int unilen;
1130 SSVAL(pdata,offset,3); /* version 3 */
1131 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1132 if(self_referral) {
1133 SSVAL(pdata,offset+4,1);
1134 } else {
1135 SSVAL(pdata,offset+4,0);
1138 /* ref_flags :use path_consumed bytes? */
1139 SSVAL(pdata,offset+6,0);
1140 SIVAL(pdata,offset+8,ref->ttl);
1142 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1143 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1144 /* copy referred path into current offset */
1145 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1146 reply_size - uni_curroffset,
1147 STR_UNICODE | STR_TERMINATE);
1148 SSVAL(pdata,offset+16,uni_curroffset-offset);
1149 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1150 memset(pdata+offset+18,'\0',16);
1152 uni_curroffset += unilen;
1153 offset += VERSION3_REFERRAL_SIZE;
1155 return reply_size;
1158 /******************************************************************
1159 Set up the DFS referral for the dfs pathname. This call returns
1160 the amount of the path covered by this server, and where the
1161 client should be redirected to. This is the meat of the
1162 TRANS2_GET_DFS_REFERRAL call.
1163 ******************************************************************/
1165 int setup_dfs_referral(connection_struct *orig_conn,
1166 const char *dfs_path,
1167 int max_referral_level,
1168 char **ppdata, NTSTATUS *pstatus)
1170 struct junction_map *junction = NULL;
1171 int consumedcnt = 0;
1172 bool self_referral = False;
1173 int reply_size = 0;
1174 char *pathnamep = NULL;
1175 char *local_dfs_path = NULL;
1176 TALLOC_CTX *ctx;
1178 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1179 *pstatus = NT_STATUS_NO_MEMORY;
1180 return -1;
1183 /* get the junction entry */
1184 if (!dfs_path) {
1185 talloc_destroy(ctx);
1186 *pstatus = NT_STATUS_NOT_FOUND;
1187 return -1;
1191 * Trim pathname sent by client so it begins with only one backslash.
1192 * Two backslashes confuse some dfs clients
1195 local_dfs_path = talloc_strdup(ctx,dfs_path);
1196 if (!local_dfs_path) {
1197 *pstatus = NT_STATUS_NO_MEMORY;
1198 talloc_destroy(ctx);
1199 return -1;
1201 pathnamep = local_dfs_path;
1202 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1203 IS_DIRECTORY_SEP(pathnamep[1])) {
1204 pathnamep++;
1207 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1208 if (!junction) {
1209 *pstatus = NT_STATUS_NO_MEMORY;
1210 talloc_destroy(ctx);
1211 return -1;
1214 /* The following call can change cwd. */
1215 *pstatus = get_referred_path(ctx, orig_conn->server_info,
1216 pathnamep, junction,
1217 &consumedcnt, &self_referral);
1218 if (!NT_STATUS_IS_OK(*pstatus)) {
1219 vfs_ChDir(orig_conn,orig_conn->connectpath);
1220 talloc_destroy(ctx);
1221 return -1;
1223 vfs_ChDir(orig_conn,orig_conn->connectpath);
1225 if (!self_referral) {
1226 pathnamep[consumedcnt] = '\0';
1228 if( DEBUGLVL( 3 ) ) {
1229 int i=0;
1230 dbgtext("setup_dfs_referral: Path %s to "
1231 "alternate path(s):",
1232 pathnamep);
1233 for(i=0;i<junction->referral_count;i++)
1234 dbgtext(" %s",
1235 junction->referral_list[i].alternate_path);
1236 dbgtext(".\n");
1240 /* create the referral depeding on version */
1241 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1243 if (max_referral_level < 2) {
1244 max_referral_level = 2;
1246 if (max_referral_level > 3) {
1247 max_referral_level = 3;
1250 switch(max_referral_level) {
1251 case 2:
1252 reply_size = setup_ver2_dfs_referral(pathnamep,
1253 ppdata, junction,
1254 self_referral);
1255 break;
1256 case 3:
1257 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1258 junction, self_referral);
1259 break;
1260 default:
1261 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1262 "version: %d\n",
1263 max_referral_level));
1264 talloc_destroy(ctx);
1265 *pstatus = NT_STATUS_INVALID_LEVEL;
1266 return -1;
1269 if (DEBUGLVL(10)) {
1270 DEBUGADD(0,("DFS Referral pdata:\n"));
1271 dump_data(0,(uint8 *)*ppdata,reply_size);
1274 talloc_destroy(ctx);
1275 *pstatus = NT_STATUS_OK;
1276 return reply_size;
1279 /**********************************************************************
1280 The following functions are called by the NETDFS RPC pipe functions
1281 **********************************************************************/
1283 /*********************************************************************
1284 Creates a junction structure from a DFS pathname
1285 **********************************************************************/
1287 bool create_junction(TALLOC_CTX *ctx,
1288 const char *dfs_path,
1289 struct junction_map *jucn)
1291 int snum;
1292 bool dummy;
1293 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1294 NTSTATUS status;
1296 if (!pdp) {
1297 return False;
1299 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1300 if (!NT_STATUS_IS_OK(status)) {
1301 return False;
1304 /* check if path is dfs : validate first token */
1305 if (!is_myname_or_ipaddr(pdp->hostname)) {
1306 DEBUG(4,("create_junction: Invalid hostname %s "
1307 "in dfs path %s\n",
1308 pdp->hostname, dfs_path));
1309 TALLOC_FREE(pdp);
1310 return False;
1313 /* Check for a non-DFS share */
1314 snum = lp_servicenumber(pdp->servicename);
1316 if(snum < 0 || !lp_msdfs_root(snum)) {
1317 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1318 pdp->servicename));
1319 TALLOC_FREE(pdp);
1320 return False;
1323 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1324 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1325 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1327 TALLOC_FREE(pdp);
1328 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1329 return False;
1331 return True;
1334 /**********************************************************************
1335 Forms a valid Unix pathname from the junction
1336 **********************************************************************/
1338 static bool junction_to_local_path(const struct junction_map *jucn,
1339 char **pp_path_out,
1340 connection_struct **conn_out,
1341 char **oldpath)
1343 int snum;
1344 NTSTATUS status;
1346 snum = lp_servicenumber(jucn->service_name);
1347 if(snum < 0) {
1348 return False;
1350 status = create_conn_struct(talloc_tos(), conn_out, snum,
1351 lp_pathname(snum), NULL, oldpath);
1352 if (!NT_STATUS_IS_OK(status)) {
1353 return False;
1356 *pp_path_out = talloc_asprintf(*conn_out,
1357 "%s/%s",
1358 lp_pathname(snum),
1359 jucn->volume_name);
1360 if (!*pp_path_out) {
1361 vfs_ChDir(*conn_out, *oldpath);
1362 conn_free(*conn_out);
1363 return False;
1365 return True;
1368 bool create_msdfs_link(const struct junction_map *jucn)
1370 char *path = NULL;
1371 char *cwd;
1372 char *msdfs_link = NULL;
1373 connection_struct *conn;
1374 int i=0;
1375 bool insert_comma = False;
1376 bool ret = False;
1378 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1379 return False;
1382 /* Form the msdfs_link contents */
1383 msdfs_link = talloc_strdup(conn, "msdfs:");
1384 if (!msdfs_link) {
1385 goto out;
1387 for(i=0; i<jucn->referral_count; i++) {
1388 char *refpath = jucn->referral_list[i].alternate_path;
1390 /* Alternate paths always use Windows separators. */
1391 trim_char(refpath, '\\', '\\');
1392 if(*refpath == '\0') {
1393 if (i == 0) {
1394 insert_comma = False;
1396 continue;
1398 if (i > 0 && insert_comma) {
1399 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1400 ",%s",
1401 refpath);
1402 } else {
1403 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1404 "%s",
1405 refpath);
1408 if (!msdfs_link) {
1409 goto out;
1411 if (!insert_comma) {
1412 insert_comma = True;
1416 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1417 path, msdfs_link));
1419 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1420 if (errno == EEXIST) {
1421 struct smb_filename *smb_fname = NULL;
1422 NTSTATUS status;
1424 status = create_synthetic_smb_fname(talloc_tos(), path,
1425 NULL, NULL,
1426 &smb_fname);
1427 if (!NT_STATUS_IS_OK(status)) {
1428 errno = map_errno_from_nt_status(status);
1429 goto out;
1432 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1433 TALLOC_FREE(smb_fname);
1434 goto out;
1436 TALLOC_FREE(smb_fname);
1438 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1439 DEBUG(1,("create_msdfs_link: symlink failed "
1440 "%s -> %s\nError: %s\n",
1441 path, msdfs_link, strerror(errno)));
1442 goto out;
1446 ret = True;
1448 out:
1449 vfs_ChDir(conn, cwd);
1450 conn_free(conn);
1451 return ret;
1454 bool remove_msdfs_link(const struct junction_map *jucn)
1456 char *path = NULL;
1457 char *cwd;
1458 connection_struct *conn;
1459 bool ret = False;
1460 struct smb_filename *smb_fname = NULL;
1461 NTSTATUS status;
1463 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1464 return false;
1467 status = create_synthetic_smb_fname(talloc_tos(), path,
1468 NULL, NULL,
1469 &smb_fname);
1470 if (!NT_STATUS_IS_OK(status)) {
1471 errno = map_errno_from_nt_status(status);
1472 return false;
1475 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1476 ret = True;
1479 TALLOC_FREE(smb_fname);
1480 vfs_ChDir(conn, cwd);
1481 conn_free(conn);
1482 return ret;
1485 /*********************************************************************
1486 Return the number of DFS links at the root of this share.
1487 *********************************************************************/
1489 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1491 size_t cnt = 0;
1492 SMB_STRUCT_DIR *dirp = NULL;
1493 const char *dname = NULL;
1494 char *talloced = NULL;
1495 const char *connect_path = lp_pathname(snum);
1496 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1497 connection_struct *conn;
1498 NTSTATUS status;
1499 char *cwd;
1501 if(*connect_path == '\0') {
1502 return 0;
1506 * Fake up a connection struct for the VFS layer.
1509 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1510 NULL, &cwd);
1511 if (!NT_STATUS_IS_OK(status)) {
1512 DEBUG(3, ("create_conn_struct failed: %s\n",
1513 nt_errstr(status)));
1514 return 0;
1517 /* Count a link for the msdfs root - convention */
1518 cnt = 1;
1520 /* No more links if this is an msdfs proxy. */
1521 if (*msdfs_proxy != '\0') {
1522 goto out;
1525 /* Now enumerate all dfs links */
1526 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1527 if(!dirp) {
1528 goto out;
1531 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1532 != NULL) {
1533 if (is_msdfs_link(conn,
1534 dname,
1535 NULL)) {
1536 cnt++;
1538 TALLOC_FREE(talloced);
1541 SMB_VFS_CLOSEDIR(conn,dirp);
1543 out:
1544 vfs_ChDir(conn, cwd);
1545 conn_free(conn);
1546 return cnt;
1549 /*********************************************************************
1550 *********************************************************************/
1552 static int form_junctions(TALLOC_CTX *ctx,
1553 int snum,
1554 struct junction_map *jucn,
1555 size_t jn_remain)
1557 size_t cnt = 0;
1558 SMB_STRUCT_DIR *dirp = NULL;
1559 const char *dname = NULL;
1560 char *talloced = NULL;
1561 const char *connect_path = lp_pathname(snum);
1562 char *service_name = lp_servicename(snum);
1563 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1564 connection_struct *conn;
1565 struct referral *ref = NULL;
1566 char *cwd;
1567 NTSTATUS status;
1569 if (jn_remain == 0) {
1570 return 0;
1573 if(*connect_path == '\0') {
1574 return 0;
1578 * Fake up a connection struct for the VFS layer.
1581 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1582 &cwd);
1583 if (!NT_STATUS_IS_OK(status)) {
1584 DEBUG(3, ("create_conn_struct failed: %s\n",
1585 nt_errstr(status)));
1586 return 0;
1589 /* form a junction for the msdfs root - convention
1590 DO NOT REMOVE THIS: NT clients will not work with us
1591 if this is not present
1593 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1594 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1595 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1596 goto out;
1598 jucn[cnt].comment = "";
1599 jucn[cnt].referral_count = 1;
1601 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1602 if (jucn[cnt].referral_list == NULL) {
1603 goto out;
1606 ref->proximity = 0;
1607 ref->ttl = REFERRAL_TTL;
1608 if (*msdfs_proxy != '\0') {
1609 ref->alternate_path = talloc_strdup(ctx,
1610 msdfs_proxy);
1611 } else {
1612 ref->alternate_path = talloc_asprintf(ctx,
1613 "\\\\%s\\%s",
1614 get_local_machine_name(),
1615 service_name);
1618 if (!ref->alternate_path) {
1619 goto out;
1621 cnt++;
1623 /* Don't enumerate if we're an msdfs proxy. */
1624 if (*msdfs_proxy != '\0') {
1625 goto out;
1628 /* Now enumerate all dfs links */
1629 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1630 if(!dirp) {
1631 goto out;
1634 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1635 != NULL) {
1636 char *link_target = NULL;
1637 if (cnt >= jn_remain) {
1638 DEBUG(2, ("form_junctions: ran out of MSDFS "
1639 "junction slots"));
1640 TALLOC_FREE(talloced);
1641 goto out;
1643 if (is_msdfs_link_internal(ctx,
1644 conn,
1645 dname, &link_target,
1646 NULL)) {
1647 if (parse_msdfs_symlink(ctx,
1648 link_target,
1649 &jucn[cnt].referral_list,
1650 &jucn[cnt].referral_count)) {
1652 jucn[cnt].service_name = talloc_strdup(ctx,
1653 service_name);
1654 jucn[cnt].volume_name = talloc_strdup(ctx,
1655 dname);
1656 if (!jucn[cnt].service_name ||
1657 !jucn[cnt].volume_name) {
1658 TALLOC_FREE(talloced);
1659 goto out;
1661 jucn[cnt].comment = "";
1662 cnt++;
1664 TALLOC_FREE(link_target);
1666 TALLOC_FREE(talloced);
1669 out:
1671 if (dirp) {
1672 SMB_VFS_CLOSEDIR(conn,dirp);
1675 vfs_ChDir(conn, cwd);
1676 conn_free(conn);
1677 return cnt;
1680 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1682 struct junction_map *jn = NULL;
1683 int i=0;
1684 size_t jn_count = 0;
1685 int sharecount = 0;
1687 *p_num_jn = 0;
1688 if(!lp_host_msdfs()) {
1689 return NULL;
1692 /* Ensure all the usershares are loaded. */
1693 become_root();
1694 load_registry_shares();
1695 sharecount = load_usershare_shares();
1696 unbecome_root();
1698 for(i=0;i < sharecount;i++) {
1699 if(lp_msdfs_root(i)) {
1700 jn_count += count_dfs_links(ctx, i);
1703 if (jn_count == 0) {
1704 return NULL;
1706 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1707 if (!jn) {
1708 return NULL;
1710 for(i=0; i < sharecount; i++) {
1711 if (*p_num_jn >= jn_count) {
1712 break;
1714 if(lp_msdfs_root(i)) {
1715 *p_num_jn += form_junctions(ctx, i,
1716 &jn[*p_num_jn],
1717 jn_count - *p_num_jn);
1720 return jn;
1723 /******************************************************************************
1724 Core function to resolve a dfs pathname possibly containing a wildcard. If
1725 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1726 detected during dfs resolution.
1727 ******************************************************************************/
1729 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1730 connection_struct *conn,
1731 bool dfs_pathnames,
1732 const char *name_in,
1733 bool allow_wcards,
1734 char **pp_name_out,
1735 bool *ppath_contains_wcard)
1737 bool path_contains_wcard;
1738 NTSTATUS status = NT_STATUS_OK;
1740 if (dfs_pathnames) {
1741 status = dfs_redirect(ctx,
1742 conn,
1743 name_in,
1744 allow_wcards,
1745 pp_name_out,
1746 &path_contains_wcard);
1748 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1749 *ppath_contains_wcard = path_contains_wcard;
1751 } else {
1753 * Cheat and just return a copy of the in ptr.
1754 * Once srvstr_get_path() uses talloc it'll
1755 * be a talloced ptr anyway.
1757 *pp_name_out = CONST_DISCARD(char *,name_in);
1759 return status;