Pass stat buffer down through all levels of VFS_READDIR wrappers
[Samba/gbeck.git] / source3 / smbd / msdfs.c
blob4d692cd2d2ba24a9d79349e5b731f3345cf104d8
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_internal(conn);
272 return status;
276 * Windows seems to insist on doing trans2getdfsreferral() calls on
277 * the IPC$ share as the anonymous user. If we try to chdir as that
278 * user we will fail.... WTF ? JRA.
281 oldcwd = vfs_GetWd(ctx, conn);
282 if (oldcwd == NULL) {
283 NTSTATUS status = map_nt_error_from_unix(errno);
284 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
285 conn_free_internal(conn);
286 return status;
289 if (vfs_ChDir(conn,conn->connectpath) != 0) {
290 NTSTATUS status = map_nt_error_from_unix(errno);
291 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
292 "Error was %s\n",
293 conn->connectpath, strerror(errno) ));
294 conn_free_internal(conn);
295 return status;
298 *pconn = conn;
299 *poldcwd = oldcwd;
301 return NT_STATUS_OK;
304 /**********************************************************************
305 Parse the contents of a symlink to verify if it is an msdfs referral
306 A valid referral is of the form:
308 msdfs:server1\share1,server2\share2
309 msdfs:server1\share1\pathname,server2\share2\pathname
310 msdfs:server1/share1,server2/share2
311 msdfs:server1/share1/pathname,server2/share2/pathname.
313 Note that the alternate paths returned here must be of the canonicalized
314 form:
316 \server\share or
317 \server\share\path\to\file,
319 even in posix path mode. This is because we have no knowledge if the
320 server we're referring to understands posix paths.
321 **********************************************************************/
323 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
324 const char *target,
325 struct referral **preflist,
326 int *refcount)
328 char *temp = NULL;
329 char *prot;
330 char **alt_path = NULL;
331 int count = 0, i;
332 struct referral *reflist;
333 char *saveptr;
335 temp = talloc_strdup(ctx, target);
336 if (!temp) {
337 return False;
339 prot = strtok_r(temp, ":", &saveptr);
340 if (!prot) {
341 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
342 return False;
345 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
346 if (!alt_path) {
347 return False;
350 /* parse out the alternate paths */
351 while((count<MAX_REFERRAL_COUNT) &&
352 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
353 count++;
356 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
358 if (count) {
359 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
360 struct referral, count);
361 if(reflist == NULL) {
362 TALLOC_FREE(alt_path);
363 return False;
365 } else {
366 reflist = *preflist = NULL;
369 for(i=0;i<count;i++) {
370 char *p;
372 /* Canonicalize link target.
373 * Replace all /'s in the path by a \ */
374 string_replace(alt_path[i], '/', '\\');
376 /* Remove leading '\\'s */
377 p = alt_path[i];
378 while (*p && (*p == '\\')) {
379 p++;
382 reflist[i].alternate_path = talloc_asprintf(ctx,
383 "\\%s",
385 if (!reflist[i].alternate_path) {
386 return False;
389 reflist[i].proximity = 0;
390 reflist[i].ttl = REFERRAL_TTL;
391 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
392 reflist[i].alternate_path));
395 *refcount = count;
397 TALLOC_FREE(alt_path);
398 return True;
401 /**********************************************************************
402 Returns true if the unix path is a valid msdfs symlink and also
403 returns the target string from inside the link.
404 **********************************************************************/
406 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
407 connection_struct *conn,
408 const char *path,
409 char **pp_link_target,
410 SMB_STRUCT_STAT *sbufp)
412 SMB_STRUCT_STAT st;
413 int referral_len = 0;
414 char link_target_buf[7];
415 size_t bufsize = 0;
416 char *link_target = NULL;
418 if (pp_link_target) {
419 bufsize = 1024;
420 link_target = TALLOC_ARRAY(ctx, char, bufsize);
421 if (!link_target) {
422 return False;
424 *pp_link_target = link_target;
425 } else {
426 bufsize = sizeof(link_target_buf);
427 link_target = link_target_buf;
430 if (sbufp == NULL) {
431 sbufp = &st;
434 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
435 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
436 path));
437 goto err;
440 if (!S_ISLNK(sbufp->st_mode)) {
441 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
442 path));
443 goto err;
446 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
447 if (referral_len == -1) {
448 DEBUG(0,("is_msdfs_link_read_target: Error reading "
449 "msdfs link %s: %s\n",
450 path, strerror(errno)));
451 goto err;
453 link_target[referral_len] = '\0';
455 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
456 link_target));
458 if (!strnequal(link_target, "msdfs:", 6)) {
459 goto err;
461 return True;
463 err:
465 if (link_target != link_target_buf) {
466 TALLOC_FREE(link_target);
468 return False;
471 /**********************************************************************
472 Returns true if the unix path is a valid msdfs symlink.
473 **********************************************************************/
475 bool is_msdfs_link(connection_struct *conn,
476 const char *path,
477 SMB_STRUCT_STAT *sbufp)
479 return is_msdfs_link_internal(talloc_tos(),
480 conn,
481 path,
482 NULL,
483 sbufp);
486 /*****************************************************************
487 Used by other functions to decide if a dfs path is remote,
488 and to get the list of referred locations for that remote path.
490 search_flag: For findfirsts, dfs links themselves are not
491 redirected, but paths beyond the links are. For normal smb calls,
492 even dfs links need to be redirected.
494 consumedcntp: how much of the dfs path is being redirected. the client
495 should try the remaining path on the redirected server.
497 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
498 link redirect are in targetpath.
499 *****************************************************************/
501 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
502 connection_struct *conn,
503 const char *dfspath, /* Incoming complete dfs path */
504 const struct dfs_path *pdp, /* Parsed out
505 server+share+extrapath. */
506 bool search_flag, /* Called from a findfirst ? */
507 int *consumedcntp,
508 char **pp_targetpath)
510 char *p = NULL;
511 char *q = NULL;
512 SMB_STRUCT_STAT sbuf;
513 NTSTATUS status;
514 char *localpath = NULL;
515 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
516 components). */
518 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
519 conn->connectpath, pdp->reqpath));
522 * Note the unix path conversion here we're doing we can
523 * throw away. We're looking for a symlink for a dfs
524 * resolution, if we don't find it we'll do another
525 * unix_convert later in the codepath.
526 * If we needed to remember what we'd resolved in
527 * dp->reqpath (as the original code did) we'd
528 * copy (localhost, dp->reqpath) on any code
529 * path below that returns True - but I don't
530 * think this is needed. JRA.
533 status = unix_convert(ctx, conn, pdp->reqpath, search_flag, &localpath,
534 NULL, &sbuf);
535 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
536 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
537 return status;
540 /* Optimization - check if we can redirect the whole path. */
542 if (is_msdfs_link_internal(ctx, conn, localpath, pp_targetpath, NULL)) {
543 if (search_flag) {
544 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
545 "for dfs link %s.\n", dfspath));
546 return NT_STATUS_OK;
549 DEBUG(6,("dfs_path_lookup: %s resolves to a "
550 "valid dfs link %s.\n", dfspath,
551 pp_targetpath ? *pp_targetpath : ""));
553 if (consumedcntp) {
554 *consumedcntp = strlen(dfspath);
556 return NT_STATUS_PATH_NOT_COVERED;
559 /* Prepare to test only for '/' components in the given path,
560 * so if a Windows path replace all '\\' characters with '/'.
561 * For a POSIX DFS path we know all separators are already '/'. */
563 canon_dfspath = talloc_strdup(ctx, dfspath);
564 if (!canon_dfspath) {
565 return NT_STATUS_NO_MEMORY;
567 if (!pdp->posix_path) {
568 string_replace(canon_dfspath, '\\', '/');
572 * localpath comes out of unix_convert, so it has
573 * no trailing backslash. Make sure that canon_dfspath hasn't either.
574 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
577 trim_char(canon_dfspath,0,'/');
580 * Redirect if any component in the path is a link.
581 * We do this by walking backwards through the
582 * local path, chopping off the last component
583 * in both the local path and the canonicalized
584 * DFS path. If we hit a DFS link then we're done.
587 p = strrchr_m(localpath, '/');
588 if (consumedcntp) {
589 q = strrchr_m(canon_dfspath, '/');
592 while (p) {
593 *p = '\0';
594 if (q) {
595 *q = '\0';
598 if (is_msdfs_link_internal(ctx, conn,
599 localpath, pp_targetpath, NULL)) {
600 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
601 "parent %s is dfs link\n", dfspath, localpath));
603 if (consumedcntp) {
604 *consumedcntp = strlen(canon_dfspath);
605 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
606 "(%d)\n",
607 canon_dfspath,
608 *consumedcntp));
611 return NT_STATUS_PATH_NOT_COVERED;
614 /* Step back on the filesystem. */
615 p = strrchr_m(localpath, '/');
617 if (consumedcntp) {
618 /* And in the canonicalized dfs path. */
619 q = strrchr_m(canon_dfspath, '/');
623 return NT_STATUS_OK;
626 /*****************************************************************
627 Decides if a dfs pathname should be redirected or not.
628 If not, the pathname is converted to a tcon-relative local unix path
630 search_wcard_flag: this flag performs 2 functions both related
631 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
632 for details.
634 This function can return NT_STATUS_OK, meaning use the returned path as-is
635 (mapped into a local path).
636 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
637 any other NT_STATUS error which is a genuine error to be
638 returned to the client.
639 *****************************************************************/
641 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
642 connection_struct *conn,
643 const char *path_in,
644 bool search_wcard_flag,
645 char **pp_path_out,
646 bool *ppath_contains_wcard)
648 NTSTATUS status;
649 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
651 if (!pdp) {
652 return NT_STATUS_NO_MEMORY;
655 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
656 ppath_contains_wcard);
657 if (!NT_STATUS_IS_OK(status)) {
658 TALLOC_FREE(pdp);
659 return status;
662 if (pdp->reqpath[0] == '\0') {
663 TALLOC_FREE(pdp);
664 *pp_path_out = talloc_strdup(ctx, "");
665 if (!*pp_path_out) {
666 return NT_STATUS_NO_MEMORY;
668 DEBUG(5,("dfs_redirect: self-referral.\n"));
669 return NT_STATUS_OK;
672 /* If dfs pathname for a non-dfs share, convert to tcon-relative
673 path and return OK */
675 if (!lp_msdfs_root(SNUM(conn))) {
676 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
677 TALLOC_FREE(pdp);
678 if (!*pp_path_out) {
679 return NT_STATUS_NO_MEMORY;
681 return NT_STATUS_OK;
684 /* If it looked like a local path (zero hostname/servicename)
685 * just treat as a tcon-relative path. */
687 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
688 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
689 TALLOC_FREE(pdp);
690 if (!*pp_path_out) {
691 return NT_STATUS_NO_MEMORY;
693 return NT_STATUS_OK;
696 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
697 || (strequal(pdp->servicename, HOMES_NAME)
698 && strequal(lp_servicename(SNUM(conn)),
699 conn->server_info->sanitized_username) )) ) {
701 /* The given sharename doesn't match this connection. */
702 TALLOC_FREE(pdp);
704 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
707 status = dfs_path_lookup(ctx, conn, path_in, pdp,
708 search_wcard_flag, NULL, NULL);
709 if (!NT_STATUS_IS_OK(status)) {
710 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
711 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
712 } else {
713 DEBUG(10,("dfs_redirect: dfs_path_lookup "
714 "failed for %s with %s\n",
715 path_in, nt_errstr(status) ));
717 return status;
720 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
722 /* Form non-dfs tcon-relative path */
723 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
724 TALLOC_FREE(pdp);
725 if (!*pp_path_out) {
726 return NT_STATUS_NO_MEMORY;
729 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
730 path_in,
731 *pp_path_out));
733 return NT_STATUS_OK;
736 /**********************************************************************
737 Return a self referral.
738 **********************************************************************/
740 static NTSTATUS self_ref(TALLOC_CTX *ctx,
741 const char *dfs_path,
742 struct junction_map *jucn,
743 int *consumedcntp,
744 bool *self_referralp)
746 struct referral *ref;
748 *self_referralp = True;
750 jucn->referral_count = 1;
751 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
752 return NT_STATUS_NO_MEMORY;
755 ref->alternate_path = talloc_strdup(ctx, dfs_path);
756 if (!ref->alternate_path) {
757 return NT_STATUS_NO_MEMORY;
759 ref->proximity = 0;
760 ref->ttl = REFERRAL_TTL;
761 jucn->referral_list = ref;
762 *consumedcntp = strlen(dfs_path);
763 return NT_STATUS_OK;
766 /**********************************************************************
767 Gets valid referrals for a dfs path and fills up the
768 junction_map structure.
769 **********************************************************************/
771 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
772 const char *dfs_path,
773 struct junction_map *jucn,
774 int *consumedcntp,
775 bool *self_referralp)
777 struct connection_struct *conn;
778 char *targetpath = NULL;
779 int snum;
780 NTSTATUS status = NT_STATUS_NOT_FOUND;
781 bool dummy;
782 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
783 char *oldpath;
785 if (!pdp) {
786 return NT_STATUS_NO_MEMORY;
789 *self_referralp = False;
791 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
792 if (!NT_STATUS_IS_OK(status)) {
793 return status;
796 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
797 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
798 if (!jucn->service_name || !jucn->volume_name) {
799 TALLOC_FREE(pdp);
800 return NT_STATUS_NO_MEMORY;
803 /* Verify the share is a dfs root */
804 snum = lp_servicenumber(jucn->service_name);
805 if(snum < 0) {
806 fstring service_name;
807 fstrcpy(service_name, jucn->service_name);
808 if ((snum = find_service(service_name)) < 0) {
809 return NT_STATUS_NOT_FOUND;
811 TALLOC_FREE(jucn->service_name);
812 jucn->service_name = talloc_strdup(ctx, service_name);
813 if (!jucn->service_name) {
814 TALLOC_FREE(pdp);
815 return NT_STATUS_NO_MEMORY;
819 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
820 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
821 "a dfs root.\n",
822 pdp->servicename, dfs_path));
823 TALLOC_FREE(pdp);
824 return NT_STATUS_NOT_FOUND;
828 * Self referrals are tested with a anonymous IPC connection and
829 * a GET_DFS_REFERRAL call to \\server\share. (which means
830 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
831 * into the directory and will fail if it cannot (as the anonymous
832 * user). Cope with this.
835 if (pdp->reqpath[0] == '\0') {
836 char *tmp;
837 struct referral *ref;
839 if (*lp_msdfs_proxy(snum) == '\0') {
840 TALLOC_FREE(pdp);
841 return self_ref(ctx,
842 dfs_path,
843 jucn,
844 consumedcntp,
845 self_referralp);
849 * It's an msdfs proxy share. Redirect to
850 * the configured target share.
853 jucn->referral_count = 1;
854 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
855 TALLOC_FREE(pdp);
856 return NT_STATUS_NO_MEMORY;
859 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
860 TALLOC_FREE(pdp);
861 return NT_STATUS_NO_MEMORY;
864 trim_string(tmp, "\\", 0);
866 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
867 TALLOC_FREE(tmp);
869 if (!ref->alternate_path) {
870 TALLOC_FREE(pdp);
871 return NT_STATUS_NO_MEMORY;
874 if (pdp->reqpath[0] != '\0') {
875 ref->alternate_path = talloc_asprintf_append(
876 ref->alternate_path,
877 "%s",
878 pdp->reqpath);
879 if (!ref->alternate_path) {
880 TALLOC_FREE(pdp);
881 return NT_STATUS_NO_MEMORY;
884 ref->proximity = 0;
885 ref->ttl = REFERRAL_TTL;
886 jucn->referral_list = ref;
887 *consumedcntp = strlen(dfs_path);
888 TALLOC_FREE(pdp);
889 return NT_STATUS_OK;
892 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
893 NULL, &oldpath);
894 if (!NT_STATUS_IS_OK(status)) {
895 TALLOC_FREE(pdp);
896 return status;
899 /* If this is a DFS path dfs_lookup should return
900 * NT_STATUS_PATH_NOT_COVERED. */
902 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
903 False, consumedcntp, &targetpath);
905 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
906 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
907 dfs_path));
908 vfs_ChDir(conn, oldpath);
909 conn_free_internal(conn);
910 TALLOC_FREE(pdp);
911 return status;
914 /* We know this is a valid dfs link. Parse the targetpath. */
915 if (!parse_msdfs_symlink(ctx, targetpath,
916 &jucn->referral_list,
917 &jucn->referral_count)) {
918 DEBUG(3,("get_referred_path: failed to parse symlink "
919 "target %s\n", targetpath ));
920 vfs_ChDir(conn, oldpath);
921 conn_free_internal(conn);
922 TALLOC_FREE(pdp);
923 return NT_STATUS_NOT_FOUND;
926 vfs_ChDir(conn, oldpath);
927 conn_free_internal(conn);
928 TALLOC_FREE(pdp);
929 return NT_STATUS_OK;
932 static int setup_ver2_dfs_referral(const char *pathname,
933 char **ppdata,
934 struct junction_map *junction,
935 bool self_referral)
937 char* pdata = *ppdata;
939 smb_ucs2_t *uni_requestedpath = NULL;
940 int uni_reqpathoffset1,uni_reqpathoffset2;
941 int uni_curroffset;
942 int requestedpathlen=0;
943 int offset;
944 int reply_size = 0;
945 int i=0;
947 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
949 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
950 &uni_requestedpath, pathname);
951 if (uni_requestedpath == NULL || requestedpathlen == 0) {
952 return -1;
955 if (DEBUGLVL(10)) {
956 dump_data(0, (unsigned char *)uni_requestedpath,
957 requestedpathlen);
960 DEBUG(10,("ref count = %u\n",junction->referral_count));
962 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
963 VERSION2_REFERRAL_SIZE * junction->referral_count;
965 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
967 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
969 reply_size = REFERRAL_HEADER_SIZE +
970 VERSION2_REFERRAL_SIZE*junction->referral_count +
971 2 * requestedpathlen;
972 DEBUG(10,("reply_size: %u\n",reply_size));
974 /* add up the unicode lengths of all the referral paths */
975 for(i=0;i<junction->referral_count;i++) {
976 DEBUG(10,("referral %u : %s\n",
978 junction->referral_list[i].alternate_path));
979 reply_size +=
980 (strlen(junction->referral_list[i].alternate_path)+1)*2;
983 DEBUG(10,("reply_size = %u\n",reply_size));
984 /* add the unexplained 0x16 bytes */
985 reply_size += 0x16;
987 pdata = (char *)SMB_REALLOC(pdata,reply_size);
988 if(pdata == NULL) {
989 DEBUG(0,("Realloc failed!\n"));
990 return -1;
992 *ppdata = pdata;
994 /* copy in the dfs requested paths.. required for offset calculations */
995 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
996 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
998 /* create the header */
999 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1000 2 byte null */
1001 /* number of referral in this pkt */
1002 SSVAL(pdata,2,junction->referral_count);
1003 if(self_referral) {
1004 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1005 } else {
1006 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1009 offset = 8;
1010 /* add the referral elements */
1011 for(i=0;i<junction->referral_count;i++) {
1012 struct referral* ref = &junction->referral_list[i];
1013 int unilen;
1015 SSVAL(pdata,offset,2); /* version 2 */
1016 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1017 if(self_referral) {
1018 SSVAL(pdata,offset+4,1);
1019 } else {
1020 SSVAL(pdata,offset+4,0);
1023 /* ref_flags :use path_consumed bytes? */
1024 SSVAL(pdata,offset+6,0);
1025 SIVAL(pdata,offset+8,ref->proximity);
1026 SIVAL(pdata,offset+12,ref->ttl);
1028 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1029 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1030 /* copy referred path into current offset */
1031 unilen = rpcstr_push(pdata+uni_curroffset,
1032 ref->alternate_path,
1033 reply_size - uni_curroffset,
1034 STR_UNICODE);
1036 SSVAL(pdata,offset+20,uni_curroffset-offset);
1038 uni_curroffset += unilen;
1039 offset += VERSION2_REFERRAL_SIZE;
1041 /* add in the unexplained 22 (0x16) bytes at the end */
1042 memset(pdata+uni_curroffset,'\0',0x16);
1043 return reply_size;
1046 static int setup_ver3_dfs_referral(const char *pathname,
1047 char **ppdata,
1048 struct junction_map *junction,
1049 bool self_referral)
1051 char *pdata = *ppdata;
1053 smb_ucs2_t *uni_reqpath = NULL;
1054 int uni_reqpathoffset1, uni_reqpathoffset2;
1055 int uni_curroffset;
1056 int reply_size = 0;
1058 int reqpathlen = 0;
1059 int offset,i=0;
1061 DEBUG(10,("setting up version3 referral\n"));
1063 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1064 if (uni_reqpath == NULL || reqpathlen == 0) {
1065 return -1;
1068 if (DEBUGLVL(10)) {
1069 dump_data(0, (unsigned char *)uni_reqpath,
1070 reqpathlen);
1073 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1074 VERSION3_REFERRAL_SIZE * junction->referral_count;
1075 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1076 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1078 for(i=0;i<junction->referral_count;i++) {
1079 DEBUG(10,("referral %u : %s\n",
1081 junction->referral_list[i].alternate_path));
1082 reply_size +=
1083 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1086 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1087 if(pdata == NULL) {
1088 DEBUG(0,("version3 referral setup:"
1089 "malloc failed for Realloc!\n"));
1090 return -1;
1092 *ppdata = pdata;
1094 /* create the header */
1095 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1096 2 byte null */
1097 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1098 if(self_referral) {
1099 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1100 } else {
1101 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1104 /* copy in the reqpaths */
1105 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1106 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1108 offset = 8;
1109 for(i=0;i<junction->referral_count;i++) {
1110 struct referral* ref = &(junction->referral_list[i]);
1111 int unilen;
1113 SSVAL(pdata,offset,3); /* version 3 */
1114 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1115 if(self_referral) {
1116 SSVAL(pdata,offset+4,1);
1117 } else {
1118 SSVAL(pdata,offset+4,0);
1121 /* ref_flags :use path_consumed bytes? */
1122 SSVAL(pdata,offset+6,0);
1123 SIVAL(pdata,offset+8,ref->ttl);
1125 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1126 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1127 /* copy referred path into current offset */
1128 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1129 reply_size - uni_curroffset,
1130 STR_UNICODE | STR_TERMINATE);
1131 SSVAL(pdata,offset+16,uni_curroffset-offset);
1132 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1133 memset(pdata+offset+18,'\0',16);
1135 uni_curroffset += unilen;
1136 offset += VERSION3_REFERRAL_SIZE;
1138 return reply_size;
1141 /******************************************************************
1142 Set up the DFS referral for the dfs pathname. This call returns
1143 the amount of the path covered by this server, and where the
1144 client should be redirected to. This is the meat of the
1145 TRANS2_GET_DFS_REFERRAL call.
1146 ******************************************************************/
1148 int setup_dfs_referral(connection_struct *orig_conn,
1149 const char *dfs_path,
1150 int max_referral_level,
1151 char **ppdata, NTSTATUS *pstatus)
1153 struct junction_map *junction = NULL;
1154 int consumedcnt = 0;
1155 bool self_referral = False;
1156 int reply_size = 0;
1157 char *pathnamep = NULL;
1158 char *local_dfs_path = NULL;
1159 TALLOC_CTX *ctx;
1161 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1162 *pstatus = NT_STATUS_NO_MEMORY;
1163 return -1;
1166 /* get the junction entry */
1167 if (!dfs_path) {
1168 talloc_destroy(ctx);
1169 *pstatus = NT_STATUS_NOT_FOUND;
1170 return -1;
1174 * Trim pathname sent by client so it begins with only one backslash.
1175 * Two backslashes confuse some dfs clients
1178 local_dfs_path = talloc_strdup(ctx,dfs_path);
1179 if (!local_dfs_path) {
1180 *pstatus = NT_STATUS_NO_MEMORY;
1181 talloc_destroy(ctx);
1182 return -1;
1184 pathnamep = local_dfs_path;
1185 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1186 IS_DIRECTORY_SEP(pathnamep[1])) {
1187 pathnamep++;
1190 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1191 if (!junction) {
1192 *pstatus = NT_STATUS_NO_MEMORY;
1193 talloc_destroy(ctx);
1194 return -1;
1197 /* The following call can change cwd. */
1198 *pstatus = get_referred_path(ctx, pathnamep, junction,
1199 &consumedcnt, &self_referral);
1200 if (!NT_STATUS_IS_OK(*pstatus)) {
1201 vfs_ChDir(orig_conn,orig_conn->connectpath);
1202 talloc_destroy(ctx);
1203 return -1;
1205 vfs_ChDir(orig_conn,orig_conn->connectpath);
1207 if (!self_referral) {
1208 pathnamep[consumedcnt] = '\0';
1210 if( DEBUGLVL( 3 ) ) {
1211 int i=0;
1212 dbgtext("setup_dfs_referral: Path %s to "
1213 "alternate path(s):",
1214 pathnamep);
1215 for(i=0;i<junction->referral_count;i++)
1216 dbgtext(" %s",
1217 junction->referral_list[i].alternate_path);
1218 dbgtext(".\n");
1222 /* create the referral depeding on version */
1223 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1225 if (max_referral_level < 2) {
1226 max_referral_level = 2;
1228 if (max_referral_level > 3) {
1229 max_referral_level = 3;
1232 switch(max_referral_level) {
1233 case 2:
1234 reply_size = setup_ver2_dfs_referral(pathnamep,
1235 ppdata, junction,
1236 self_referral);
1237 break;
1238 case 3:
1239 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1240 junction, self_referral);
1241 break;
1242 default:
1243 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1244 "version: %d\n",
1245 max_referral_level));
1246 talloc_destroy(ctx);
1247 *pstatus = NT_STATUS_INVALID_LEVEL;
1248 return -1;
1251 if (DEBUGLVL(10)) {
1252 DEBUGADD(0,("DFS Referral pdata:\n"));
1253 dump_data(0,(uint8 *)*ppdata,reply_size);
1256 talloc_destroy(ctx);
1257 *pstatus = NT_STATUS_OK;
1258 return reply_size;
1261 /**********************************************************************
1262 The following functions are called by the NETDFS RPC pipe functions
1263 **********************************************************************/
1265 /*********************************************************************
1266 Creates a junction structure from a DFS pathname
1267 **********************************************************************/
1269 bool create_junction(TALLOC_CTX *ctx,
1270 const char *dfs_path,
1271 struct junction_map *jucn)
1273 int snum;
1274 bool dummy;
1275 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1276 NTSTATUS status;
1278 if (!pdp) {
1279 return False;
1281 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1282 if (!NT_STATUS_IS_OK(status)) {
1283 return False;
1286 /* check if path is dfs : validate first token */
1287 if (!is_myname_or_ipaddr(pdp->hostname)) {
1288 DEBUG(4,("create_junction: Invalid hostname %s "
1289 "in dfs path %s\n",
1290 pdp->hostname, dfs_path));
1291 TALLOC_FREE(pdp);
1292 return False;
1295 /* Check for a non-DFS share */
1296 snum = lp_servicenumber(pdp->servicename);
1298 if(snum < 0 || !lp_msdfs_root(snum)) {
1299 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1300 pdp->servicename));
1301 TALLOC_FREE(pdp);
1302 return False;
1305 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1306 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1307 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1309 TALLOC_FREE(pdp);
1310 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1311 return False;
1313 return True;
1316 /**********************************************************************
1317 Forms a valid Unix pathname from the junction
1318 **********************************************************************/
1320 static bool junction_to_local_path(const struct junction_map *jucn,
1321 char **pp_path_out,
1322 connection_struct **conn_out,
1323 char **oldpath)
1325 int snum;
1326 NTSTATUS status;
1328 snum = lp_servicenumber(jucn->service_name);
1329 if(snum < 0) {
1330 return False;
1332 status = create_conn_struct(talloc_tos(), conn_out, snum,
1333 lp_pathname(snum), NULL, oldpath);
1334 if (!NT_STATUS_IS_OK(status)) {
1335 return False;
1338 *pp_path_out = talloc_asprintf(*conn_out,
1339 "%s/%s",
1340 lp_pathname(snum),
1341 jucn->volume_name);
1342 if (!*pp_path_out) {
1343 vfs_ChDir(*conn_out, *oldpath);
1344 conn_free_internal(*conn_out);
1345 return False;
1347 return True;
1350 bool create_msdfs_link(const struct junction_map *jucn)
1352 char *path = NULL;
1353 char *cwd;
1354 char *msdfs_link = NULL;
1355 connection_struct *conn;
1356 int i=0;
1357 bool insert_comma = False;
1358 bool ret = False;
1360 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1361 return False;
1364 /* Form the msdfs_link contents */
1365 msdfs_link = talloc_strdup(conn, "msdfs:");
1366 if (!msdfs_link) {
1367 goto out;
1369 for(i=0; i<jucn->referral_count; i++) {
1370 char *refpath = jucn->referral_list[i].alternate_path;
1372 /* Alternate paths always use Windows separators. */
1373 trim_char(refpath, '\\', '\\');
1374 if(*refpath == '\0') {
1375 if (i == 0) {
1376 insert_comma = False;
1378 continue;
1380 if (i > 0 && insert_comma) {
1381 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1382 ",%s",
1383 refpath);
1384 } else {
1385 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1386 "%s",
1387 refpath);
1390 if (!msdfs_link) {
1391 goto out;
1393 if (!insert_comma) {
1394 insert_comma = True;
1398 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1399 path, msdfs_link));
1401 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1402 if (errno == EEXIST) {
1403 if(SMB_VFS_UNLINK(conn,path)!=0) {
1404 goto out;
1407 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1408 DEBUG(1,("create_msdfs_link: symlink failed "
1409 "%s -> %s\nError: %s\n",
1410 path, msdfs_link, strerror(errno)));
1411 goto out;
1415 ret = True;
1417 out:
1418 vfs_ChDir(conn, cwd);
1419 conn_free_internal(conn);
1420 return ret;
1423 bool remove_msdfs_link(const struct junction_map *jucn)
1425 char *path = NULL;
1426 char *cwd;
1427 connection_struct *conn;
1428 bool ret = False;
1430 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1431 return false;
1434 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1435 ret = True;
1438 vfs_ChDir(conn, cwd);
1439 conn_free_internal(conn);
1440 return ret;
1443 /*********************************************************************
1444 Return the number of DFS links at the root of this share.
1445 *********************************************************************/
1447 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1449 size_t cnt = 0;
1450 SMB_STRUCT_DIR *dirp = NULL;
1451 char *dname = NULL;
1452 const char *connect_path = lp_pathname(snum);
1453 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1454 connection_struct *conn;
1455 NTSTATUS status;
1456 char *cwd;
1458 if(*connect_path == '\0') {
1459 return 0;
1463 * Fake up a connection struct for the VFS layer.
1466 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1467 NULL, &cwd);
1468 if (!NT_STATUS_IS_OK(status)) {
1469 DEBUG(3, ("create_conn_struct failed: %s\n",
1470 nt_errstr(status)));
1471 return 0;
1474 /* Count a link for the msdfs root - convention */
1475 cnt = 1;
1477 /* No more links if this is an msdfs proxy. */
1478 if (*msdfs_proxy != '\0') {
1479 goto out;
1482 /* Now enumerate all dfs links */
1483 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1484 if(!dirp) {
1485 goto out;
1488 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1489 if (is_msdfs_link(conn,
1490 dname,
1491 NULL)) {
1492 cnt++;
1496 SMB_VFS_CLOSEDIR(conn,dirp);
1498 out:
1499 vfs_ChDir(conn, cwd);
1500 conn_free_internal(conn);
1501 return cnt;
1504 /*********************************************************************
1505 *********************************************************************/
1507 static int form_junctions(TALLOC_CTX *ctx,
1508 int snum,
1509 struct junction_map *jucn,
1510 size_t jn_remain)
1512 size_t cnt = 0;
1513 SMB_STRUCT_DIR *dirp = NULL;
1514 char *dname = NULL;
1515 const char *connect_path = lp_pathname(snum);
1516 char *service_name = lp_servicename(snum);
1517 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1518 connection_struct *conn;
1519 struct referral *ref = NULL;
1520 char *cwd;
1521 NTSTATUS status;
1523 if (jn_remain == 0) {
1524 return 0;
1527 if(*connect_path == '\0') {
1528 return 0;
1532 * Fake up a connection struct for the VFS layer.
1535 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1536 &cwd);
1537 if (!NT_STATUS_IS_OK(status)) {
1538 DEBUG(3, ("create_conn_struct failed: %s\n",
1539 nt_errstr(status)));
1540 return 0;
1543 /* form a junction for the msdfs root - convention
1544 DO NOT REMOVE THIS: NT clients will not work with us
1545 if this is not present
1547 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1548 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1549 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1550 goto out;
1552 jucn[cnt].comment = "";
1553 jucn[cnt].referral_count = 1;
1555 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1556 if (jucn[cnt].referral_list == NULL) {
1557 goto out;
1560 ref->proximity = 0;
1561 ref->ttl = REFERRAL_TTL;
1562 if (*msdfs_proxy != '\0') {
1563 ref->alternate_path = talloc_strdup(ctx,
1564 msdfs_proxy);
1565 } else {
1566 ref->alternate_path = talloc_asprintf(ctx,
1567 "\\\\%s\\%s",
1568 get_local_machine_name(),
1569 service_name);
1572 if (!ref->alternate_path) {
1573 goto out;
1575 cnt++;
1577 /* Don't enumerate if we're an msdfs proxy. */
1578 if (*msdfs_proxy != '\0') {
1579 goto out;
1582 /* Now enumerate all dfs links */
1583 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1584 if(!dirp) {
1585 goto out;
1588 while ((dname = vfs_readdirname(conn, dirp, NULL)) != NULL) {
1589 char *link_target = NULL;
1590 if (cnt >= jn_remain) {
1591 DEBUG(2, ("form_junctions: ran out of MSDFS "
1592 "junction slots"));
1593 goto out;
1595 if (is_msdfs_link_internal(ctx,
1596 conn,
1597 dname, &link_target,
1598 NULL)) {
1599 if (parse_msdfs_symlink(ctx,
1600 link_target,
1601 &jucn[cnt].referral_list,
1602 &jucn[cnt].referral_count)) {
1604 jucn[cnt].service_name = talloc_strdup(ctx,
1605 service_name);
1606 jucn[cnt].volume_name = talloc_strdup(ctx,
1607 dname);
1608 if (!jucn[cnt].service_name ||
1609 !jucn[cnt].volume_name) {
1610 goto out;
1612 jucn[cnt].comment = "";
1613 cnt++;
1615 TALLOC_FREE(link_target);
1619 out:
1621 if (dirp) {
1622 SMB_VFS_CLOSEDIR(conn,dirp);
1625 vfs_ChDir(conn, cwd);
1626 conn_free_internal(conn);
1627 return cnt;
1630 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1632 struct junction_map *jn = NULL;
1633 int i=0;
1634 size_t jn_count = 0;
1635 int sharecount = 0;
1637 *p_num_jn = 0;
1638 if(!lp_host_msdfs()) {
1639 return NULL;
1642 /* Ensure all the usershares are loaded. */
1643 become_root();
1644 load_registry_shares();
1645 sharecount = load_usershare_shares();
1646 unbecome_root();
1648 for(i=0;i < sharecount;i++) {
1649 if(lp_msdfs_root(i)) {
1650 jn_count += count_dfs_links(ctx, i);
1653 if (jn_count == 0) {
1654 return NULL;
1656 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1657 if (!jn) {
1658 return NULL;
1660 for(i=0; i < sharecount; i++) {
1661 if (*p_num_jn >= jn_count) {
1662 break;
1664 if(lp_msdfs_root(i)) {
1665 *p_num_jn += form_junctions(ctx, i,
1666 &jn[*p_num_jn],
1667 jn_count - *p_num_jn);
1670 return jn;
1673 /******************************************************************************
1674 Core function to resolve a dfs pathname.
1675 ******************************************************************************/
1677 NTSTATUS resolve_dfspath(TALLOC_CTX *ctx,
1678 connection_struct *conn,
1679 bool dfs_pathnames,
1680 const char *name_in,
1681 char **pp_name_out)
1683 NTSTATUS status = NT_STATUS_OK;
1684 bool dummy;
1685 if (dfs_pathnames) {
1686 status = dfs_redirect(ctx,
1687 conn,
1688 name_in,
1689 False,
1690 pp_name_out,
1691 &dummy);
1692 } else {
1694 * Cheat and just return a copy of the in ptr.
1695 * Once srvstr_get_path() uses talloc it'll
1696 * be a talloced ptr anyway.
1698 *pp_name_out = CONST_DISCARD(char *,name_in);
1700 return status;
1703 /******************************************************************************
1704 Core function to resolve a dfs pathname possibly containing a wildcard.
1705 This function is identical to the above except for the bool param to
1706 dfs_redirect but I need this to be separate so it's really clear when
1707 we're allowing wildcards and when we're not. JRA.
1708 ******************************************************************************/
1710 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1711 connection_struct *conn,
1712 bool dfs_pathnames,
1713 const char *name_in,
1714 char **pp_name_out,
1715 bool *ppath_contains_wcard)
1717 NTSTATUS status = NT_STATUS_OK;
1718 if (dfs_pathnames) {
1719 status = dfs_redirect(ctx,
1720 conn,
1721 name_in,
1722 True,
1723 pp_name_out,
1724 ppath_contains_wcard);
1725 } else {
1727 * Cheat and just return a copy of the in ptr.
1728 * Once srvstr_get_path() uses talloc it'll
1729 * be a talloced ptr anyway.
1731 *pp_name_out = CONST_DISCARD(char *,name_in);
1733 return status;