s3:smbd: add file_set_sparse() function
[Samba/wip.git] / source3 / smbd / msdfs.c
blob165e802a82c727a951e22ae55e5d747220ed6854
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->using_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 const 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 conn->sconn = smbd_server_conn;
259 conn->sconn->num_tcons_open++;
261 if (server_info != NULL) {
262 conn->server_info = copy_serverinfo(conn, server_info);
263 if (conn->server_info == NULL) {
264 DEBUG(0, ("copy_serverinfo failed\n"));
265 TALLOC_FREE(conn);
266 return NT_STATUS_NO_MEMORY;
270 set_conn_connectpath(conn, connpath);
272 if (!smbd_vfs_init(conn)) {
273 NTSTATUS status = map_nt_error_from_unix(errno);
274 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
275 conn_free(conn);
276 return status;
279 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
282 * Windows seems to insist on doing trans2getdfsreferral() calls on
283 * the IPC$ share as the anonymous user. If we try to chdir as that
284 * user we will fail.... WTF ? JRA.
287 oldcwd = vfs_GetWd(ctx, conn);
288 if (oldcwd == NULL) {
289 NTSTATUS status = map_nt_error_from_unix(errno);
290 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
291 conn_free(conn);
292 return status;
295 if (vfs_ChDir(conn,conn->connectpath) != 0) {
296 NTSTATUS status = map_nt_error_from_unix(errno);
297 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
298 "Error was %s\n",
299 conn->connectpath, strerror(errno) ));
300 conn_free(conn);
301 return status;
304 *pconn = conn;
305 *poldcwd = oldcwd;
307 return NT_STATUS_OK;
310 /**********************************************************************
311 Parse the contents of a symlink to verify if it is an msdfs referral
312 A valid referral is of the form:
314 msdfs:server1\share1,server2\share2
315 msdfs:server1\share1\pathname,server2\share2\pathname
316 msdfs:server1/share1,server2/share2
317 msdfs:server1/share1/pathname,server2/share2/pathname.
319 Note that the alternate paths returned here must be of the canonicalized
320 form:
322 \server\share or
323 \server\share\path\to\file,
325 even in posix path mode. This is because we have no knowledge if the
326 server we're referring to understands posix paths.
327 **********************************************************************/
329 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
330 const char *target,
331 struct referral **preflist,
332 int *refcount)
334 char *temp = NULL;
335 char *prot;
336 char **alt_path = NULL;
337 int count = 0, i;
338 struct referral *reflist;
339 char *saveptr;
341 temp = talloc_strdup(ctx, target);
342 if (!temp) {
343 return False;
345 prot = strtok_r(temp, ":", &saveptr);
346 if (!prot) {
347 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
348 return False;
351 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
352 if (!alt_path) {
353 return False;
356 /* parse out the alternate paths */
357 while((count<MAX_REFERRAL_COUNT) &&
358 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
359 count++;
362 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
364 if (count) {
365 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
366 struct referral, count);
367 if(reflist == NULL) {
368 TALLOC_FREE(alt_path);
369 return False;
371 } else {
372 reflist = *preflist = NULL;
375 for(i=0;i<count;i++) {
376 char *p;
378 /* Canonicalize link target.
379 * Replace all /'s in the path by a \ */
380 string_replace(alt_path[i], '/', '\\');
382 /* Remove leading '\\'s */
383 p = alt_path[i];
384 while (*p && (*p == '\\')) {
385 p++;
388 reflist[i].alternate_path = talloc_asprintf(ctx,
389 "\\%s",
391 if (!reflist[i].alternate_path) {
392 return False;
395 reflist[i].proximity = 0;
396 reflist[i].ttl = REFERRAL_TTL;
397 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
398 reflist[i].alternate_path));
401 *refcount = count;
403 TALLOC_FREE(alt_path);
404 return True;
407 /**********************************************************************
408 Returns true if the unix path is a valid msdfs symlink and also
409 returns the target string from inside the link.
410 **********************************************************************/
412 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
413 connection_struct *conn,
414 const char *path,
415 char **pp_link_target,
416 SMB_STRUCT_STAT *sbufp)
418 int referral_len = 0;
419 #if defined(HAVE_BROKEN_READLINK)
420 char link_target_buf[PATH_MAX];
421 #else
422 char link_target_buf[7];
423 #endif
424 size_t bufsize = 0;
425 char *link_target = NULL;
426 struct smb_filename smb_fname;
428 if (pp_link_target) {
429 bufsize = 1024;
430 link_target = TALLOC_ARRAY(ctx, char, bufsize);
431 if (!link_target) {
432 return False;
434 *pp_link_target = link_target;
435 } else {
436 bufsize = sizeof(link_target_buf);
437 link_target = link_target_buf;
440 ZERO_STRUCT(smb_fname);
441 smb_fname.base_name = discard_const_p(char, path);
443 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
444 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
445 path));
446 goto err;
448 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
449 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
450 path));
451 goto err;
453 if (sbufp != NULL) {
454 *sbufp = smb_fname.st;
457 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
458 if (referral_len == -1) {
459 DEBUG(0,("is_msdfs_link_read_target: Error reading "
460 "msdfs link %s: %s\n",
461 path, strerror(errno)));
462 goto err;
464 link_target[referral_len] = '\0';
466 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
467 link_target));
469 if (!strnequal(link_target, "msdfs:", 6)) {
470 goto err;
472 return True;
474 err:
476 if (link_target != link_target_buf) {
477 TALLOC_FREE(link_target);
479 return False;
482 /**********************************************************************
483 Returns true if the unix path is a valid msdfs symlink.
484 **********************************************************************/
486 bool is_msdfs_link(connection_struct *conn,
487 const char *path,
488 SMB_STRUCT_STAT *sbufp)
490 return is_msdfs_link_internal(talloc_tos(),
491 conn,
492 path,
493 NULL,
494 sbufp);
497 /*****************************************************************
498 Used by other functions to decide if a dfs path is remote,
499 and to get the list of referred locations for that remote path.
501 search_flag: For findfirsts, dfs links themselves are not
502 redirected, but paths beyond the links are. For normal smb calls,
503 even dfs links need to be redirected.
505 consumedcntp: how much of the dfs path is being redirected. the client
506 should try the remaining path on the redirected server.
508 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
509 link redirect are in targetpath.
510 *****************************************************************/
512 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
513 connection_struct *conn,
514 const char *dfspath, /* Incoming complete dfs path */
515 const struct dfs_path *pdp, /* Parsed out
516 server+share+extrapath. */
517 bool search_flag, /* Called from a findfirst ? */
518 int *consumedcntp,
519 char **pp_targetpath)
521 char *p = NULL;
522 char *q = NULL;
523 NTSTATUS status;
524 struct smb_filename *smb_fname = NULL;
525 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
526 components). */
528 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
529 conn->connectpath, pdp->reqpath));
532 * Note the unix path conversion here we're doing we can
533 * throw away. We're looking for a symlink for a dfs
534 * resolution, if we don't find it we'll do another
535 * unix_convert later in the codepath.
536 * If we needed to remember what we'd resolved in
537 * dp->reqpath (as the original code did) we'd
538 * copy (localhost, dp->reqpath) on any code
539 * path below that returns True - but I don't
540 * think this is needed. JRA.
543 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
544 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
546 if (!NT_STATUS_IS_OK(status)) {
547 if (!NT_STATUS_EQUAL(status,
548 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
549 return status;
552 /* Create an smb_fname to use below. */
553 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
554 NULL, &smb_fname);
555 if (!NT_STATUS_IS_OK(status)) {
556 return status;
560 /* Optimization - check if we can redirect the whole path. */
562 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
563 pp_targetpath, NULL)) {
564 if (search_flag) {
565 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
566 "for dfs link %s.\n", dfspath));
567 status = NT_STATUS_OK;
568 goto out;
571 DEBUG(6,("dfs_path_lookup: %s resolves to a "
572 "valid dfs link %s.\n", dfspath,
573 pp_targetpath ? *pp_targetpath : ""));
575 if (consumedcntp) {
576 *consumedcntp = strlen(dfspath);
578 status = NT_STATUS_PATH_NOT_COVERED;
579 goto out;
582 /* Prepare to test only for '/' components in the given path,
583 * so if a Windows path replace all '\\' characters with '/'.
584 * For a POSIX DFS path we know all separators are already '/'. */
586 canon_dfspath = talloc_strdup(ctx, dfspath);
587 if (!canon_dfspath) {
588 status = NT_STATUS_NO_MEMORY;
589 goto out;
591 if (!pdp->posix_path) {
592 string_replace(canon_dfspath, '\\', '/');
596 * localpath comes out of unix_convert, so it has
597 * no trailing backslash. Make sure that canon_dfspath hasn't either.
598 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
601 trim_char(canon_dfspath,0,'/');
604 * Redirect if any component in the path is a link.
605 * We do this by walking backwards through the
606 * local path, chopping off the last component
607 * in both the local path and the canonicalized
608 * DFS path. If we hit a DFS link then we're done.
611 p = strrchr_m(smb_fname->base_name, '/');
612 if (consumedcntp) {
613 q = strrchr_m(canon_dfspath, '/');
616 while (p) {
617 *p = '\0';
618 if (q) {
619 *q = '\0';
622 if (is_msdfs_link_internal(ctx, conn,
623 smb_fname->base_name, pp_targetpath,
624 NULL)) {
625 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
626 "parent %s is dfs link\n", dfspath,
627 smb_fname_str_dbg(smb_fname)));
629 if (consumedcntp) {
630 *consumedcntp = strlen(canon_dfspath);
631 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
632 "(%d)\n",
633 canon_dfspath,
634 *consumedcntp));
637 status = NT_STATUS_PATH_NOT_COVERED;
638 goto out;
641 /* Step back on the filesystem. */
642 p = strrchr_m(smb_fname->base_name, '/');
644 if (consumedcntp) {
645 /* And in the canonicalized dfs path. */
646 q = strrchr_m(canon_dfspath, '/');
650 status = NT_STATUS_OK;
651 out:
652 TALLOC_FREE(smb_fname);
653 return status;
656 /*****************************************************************
657 Decides if a dfs pathname should be redirected or not.
658 If not, the pathname is converted to a tcon-relative local unix path
660 search_wcard_flag: this flag performs 2 functions both related
661 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
662 for details.
664 This function can return NT_STATUS_OK, meaning use the returned path as-is
665 (mapped into a local path).
666 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
667 any other NT_STATUS error which is a genuine error to be
668 returned to the client.
669 *****************************************************************/
671 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
672 connection_struct *conn,
673 const char *path_in,
674 bool search_wcard_flag,
675 char **pp_path_out,
676 bool *ppath_contains_wcard)
678 NTSTATUS status;
679 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
681 if (!pdp) {
682 return NT_STATUS_NO_MEMORY;
685 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
686 ppath_contains_wcard);
687 if (!NT_STATUS_IS_OK(status)) {
688 TALLOC_FREE(pdp);
689 return status;
692 if (pdp->reqpath[0] == '\0') {
693 TALLOC_FREE(pdp);
694 *pp_path_out = talloc_strdup(ctx, "");
695 if (!*pp_path_out) {
696 return NT_STATUS_NO_MEMORY;
698 DEBUG(5,("dfs_redirect: self-referral.\n"));
699 return NT_STATUS_OK;
702 /* If dfs pathname for a non-dfs share, convert to tcon-relative
703 path and return OK */
705 if (!lp_msdfs_root(SNUM(conn))) {
706 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
707 TALLOC_FREE(pdp);
708 if (!*pp_path_out) {
709 return NT_STATUS_NO_MEMORY;
711 return NT_STATUS_OK;
714 /* If it looked like a local path (zero hostname/servicename)
715 * just treat as a tcon-relative path. */
717 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
718 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
719 TALLOC_FREE(pdp);
720 if (!*pp_path_out) {
721 return NT_STATUS_NO_MEMORY;
723 return NT_STATUS_OK;
726 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
727 || (strequal(pdp->servicename, HOMES_NAME)
728 && strequal(lp_servicename(SNUM(conn)),
729 conn->server_info->sanitized_username) )) ) {
731 /* The given sharename doesn't match this connection. */
732 TALLOC_FREE(pdp);
734 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
737 status = dfs_path_lookup(ctx, conn, path_in, pdp,
738 search_wcard_flag, NULL, NULL);
739 if (!NT_STATUS_IS_OK(status)) {
740 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
741 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
742 } else {
743 DEBUG(10,("dfs_redirect: dfs_path_lookup "
744 "failed for %s with %s\n",
745 path_in, nt_errstr(status) ));
747 return status;
750 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
752 /* Form non-dfs tcon-relative path */
753 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
754 TALLOC_FREE(pdp);
755 if (!*pp_path_out) {
756 return NT_STATUS_NO_MEMORY;
759 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
760 path_in,
761 *pp_path_out));
763 return NT_STATUS_OK;
766 /**********************************************************************
767 Return a self referral.
768 **********************************************************************/
770 static NTSTATUS self_ref(TALLOC_CTX *ctx,
771 const char *dfs_path,
772 struct junction_map *jucn,
773 int *consumedcntp,
774 bool *self_referralp)
776 struct referral *ref;
778 *self_referralp = True;
780 jucn->referral_count = 1;
781 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
782 return NT_STATUS_NO_MEMORY;
785 ref->alternate_path = talloc_strdup(ctx, dfs_path);
786 if (!ref->alternate_path) {
787 return NT_STATUS_NO_MEMORY;
789 ref->proximity = 0;
790 ref->ttl = REFERRAL_TTL;
791 jucn->referral_list = ref;
792 *consumedcntp = strlen(dfs_path);
793 return NT_STATUS_OK;
796 /**********************************************************************
797 Gets valid referrals for a dfs path and fills up the
798 junction_map structure.
799 **********************************************************************/
801 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
802 const char *dfs_path,
803 struct junction_map *jucn,
804 int *consumedcntp,
805 bool *self_referralp)
807 struct connection_struct *conn;
808 char *targetpath = NULL;
809 int snum;
810 NTSTATUS status = NT_STATUS_NOT_FOUND;
811 bool dummy;
812 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
813 char *oldpath;
815 if (!pdp) {
816 return NT_STATUS_NO_MEMORY;
819 *self_referralp = False;
821 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
822 if (!NT_STATUS_IS_OK(status)) {
823 return status;
826 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
827 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
828 if (!jucn->service_name || !jucn->volume_name) {
829 TALLOC_FREE(pdp);
830 return NT_STATUS_NO_MEMORY;
833 /* Verify the share is a dfs root */
834 snum = lp_servicenumber(jucn->service_name);
835 if(snum < 0) {
836 char *service_name = NULL;
837 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
838 return NT_STATUS_NOT_FOUND;
840 if (!service_name) {
841 return NT_STATUS_NO_MEMORY;
843 TALLOC_FREE(jucn->service_name);
844 jucn->service_name = talloc_strdup(ctx, service_name);
845 if (!jucn->service_name) {
846 TALLOC_FREE(pdp);
847 return NT_STATUS_NO_MEMORY;
851 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
852 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
853 "a dfs root.\n",
854 pdp->servicename, dfs_path));
855 TALLOC_FREE(pdp);
856 return NT_STATUS_NOT_FOUND;
860 * Self referrals are tested with a anonymous IPC connection and
861 * a GET_DFS_REFERRAL call to \\server\share. (which means
862 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
863 * into the directory and will fail if it cannot (as the anonymous
864 * user). Cope with this.
867 if (pdp->reqpath[0] == '\0') {
868 char *tmp;
869 struct referral *ref;
871 if (*lp_msdfs_proxy(snum) == '\0') {
872 TALLOC_FREE(pdp);
873 return self_ref(ctx,
874 dfs_path,
875 jucn,
876 consumedcntp,
877 self_referralp);
881 * It's an msdfs proxy share. Redirect to
882 * the configured target share.
885 jucn->referral_count = 1;
886 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
887 TALLOC_FREE(pdp);
888 return NT_STATUS_NO_MEMORY;
891 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
892 TALLOC_FREE(pdp);
893 return NT_STATUS_NO_MEMORY;
896 trim_string(tmp, "\\", 0);
898 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
899 TALLOC_FREE(tmp);
901 if (!ref->alternate_path) {
902 TALLOC_FREE(pdp);
903 return NT_STATUS_NO_MEMORY;
906 if (pdp->reqpath[0] != '\0') {
907 ref->alternate_path = talloc_asprintf_append(
908 ref->alternate_path,
909 "%s",
910 pdp->reqpath);
911 if (!ref->alternate_path) {
912 TALLOC_FREE(pdp);
913 return NT_STATUS_NO_MEMORY;
916 ref->proximity = 0;
917 ref->ttl = REFERRAL_TTL;
918 jucn->referral_list = ref;
919 *consumedcntp = strlen(dfs_path);
920 TALLOC_FREE(pdp);
921 return NT_STATUS_OK;
924 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
925 NULL, &oldpath);
926 if (!NT_STATUS_IS_OK(status)) {
927 TALLOC_FREE(pdp);
928 return status;
931 /* If this is a DFS path dfs_lookup should return
932 * NT_STATUS_PATH_NOT_COVERED. */
934 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
935 False, consumedcntp, &targetpath);
937 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
938 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
939 dfs_path));
940 vfs_ChDir(conn, oldpath);
941 conn_free(conn);
942 TALLOC_FREE(pdp);
943 return status;
946 /* We know this is a valid dfs link. Parse the targetpath. */
947 if (!parse_msdfs_symlink(ctx, targetpath,
948 &jucn->referral_list,
949 &jucn->referral_count)) {
950 DEBUG(3,("get_referred_path: failed to parse symlink "
951 "target %s\n", targetpath ));
952 vfs_ChDir(conn, oldpath);
953 conn_free(conn);
954 TALLOC_FREE(pdp);
955 return NT_STATUS_NOT_FOUND;
958 vfs_ChDir(conn, oldpath);
959 conn_free(conn);
960 TALLOC_FREE(pdp);
961 return NT_STATUS_OK;
964 static int setup_ver2_dfs_referral(const char *pathname,
965 char **ppdata,
966 struct junction_map *junction,
967 bool self_referral)
969 char* pdata = *ppdata;
971 smb_ucs2_t *uni_requestedpath = NULL;
972 int uni_reqpathoffset1,uni_reqpathoffset2;
973 int uni_curroffset;
974 int requestedpathlen=0;
975 int offset;
976 int reply_size = 0;
977 int i=0;
979 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
981 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
982 &uni_requestedpath, pathname);
983 if (uni_requestedpath == NULL || requestedpathlen == 0) {
984 return -1;
987 if (DEBUGLVL(10)) {
988 dump_data(0, (unsigned char *)uni_requestedpath,
989 requestedpathlen);
992 DEBUG(10,("ref count = %u\n",junction->referral_count));
994 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
995 VERSION2_REFERRAL_SIZE * junction->referral_count;
997 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
999 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1001 reply_size = REFERRAL_HEADER_SIZE +
1002 VERSION2_REFERRAL_SIZE*junction->referral_count +
1003 2 * requestedpathlen;
1004 DEBUG(10,("reply_size: %u\n",reply_size));
1006 /* add up the unicode lengths of all the referral paths */
1007 for(i=0;i<junction->referral_count;i++) {
1008 DEBUG(10,("referral %u : %s\n",
1010 junction->referral_list[i].alternate_path));
1011 reply_size +=
1012 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1015 DEBUG(10,("reply_size = %u\n",reply_size));
1016 /* add the unexplained 0x16 bytes */
1017 reply_size += 0x16;
1019 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1020 if(pdata == NULL) {
1021 DEBUG(0,("Realloc failed!\n"));
1022 return -1;
1024 *ppdata = pdata;
1026 /* copy in the dfs requested paths.. required for offset calculations */
1027 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1028 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1030 /* create the header */
1031 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1032 2 byte null */
1033 /* number of referral in this pkt */
1034 SSVAL(pdata,2,junction->referral_count);
1035 if(self_referral) {
1036 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1037 } else {
1038 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1041 offset = 8;
1042 /* add the referral elements */
1043 for(i=0;i<junction->referral_count;i++) {
1044 struct referral* ref = &junction->referral_list[i];
1045 int unilen;
1047 SSVAL(pdata,offset,2); /* version 2 */
1048 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1049 if(self_referral) {
1050 SSVAL(pdata,offset+4,1);
1051 } else {
1052 SSVAL(pdata,offset+4,0);
1055 /* ref_flags :use path_consumed bytes? */
1056 SSVAL(pdata,offset+6,0);
1057 SIVAL(pdata,offset+8,ref->proximity);
1058 SIVAL(pdata,offset+12,ref->ttl);
1060 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1061 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1062 /* copy referred path into current offset */
1063 unilen = rpcstr_push(pdata+uni_curroffset,
1064 ref->alternate_path,
1065 reply_size - uni_curroffset,
1066 STR_UNICODE);
1068 SSVAL(pdata,offset+20,uni_curroffset-offset);
1070 uni_curroffset += unilen;
1071 offset += VERSION2_REFERRAL_SIZE;
1073 /* add in the unexplained 22 (0x16) bytes at the end */
1074 memset(pdata+uni_curroffset,'\0',0x16);
1075 return reply_size;
1078 static int setup_ver3_dfs_referral(const char *pathname,
1079 char **ppdata,
1080 struct junction_map *junction,
1081 bool self_referral)
1083 char *pdata = *ppdata;
1085 smb_ucs2_t *uni_reqpath = NULL;
1086 int uni_reqpathoffset1, uni_reqpathoffset2;
1087 int uni_curroffset;
1088 int reply_size = 0;
1090 int reqpathlen = 0;
1091 int offset,i=0;
1093 DEBUG(10,("setting up version3 referral\n"));
1095 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1096 if (uni_reqpath == NULL || reqpathlen == 0) {
1097 return -1;
1100 if (DEBUGLVL(10)) {
1101 dump_data(0, (unsigned char *)uni_reqpath,
1102 reqpathlen);
1105 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1106 VERSION3_REFERRAL_SIZE * junction->referral_count;
1107 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1108 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1110 for(i=0;i<junction->referral_count;i++) {
1111 DEBUG(10,("referral %u : %s\n",
1113 junction->referral_list[i].alternate_path));
1114 reply_size +=
1115 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1118 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1119 if(pdata == NULL) {
1120 DEBUG(0,("version3 referral setup:"
1121 "malloc failed for Realloc!\n"));
1122 return -1;
1124 *ppdata = pdata;
1126 /* create the header */
1127 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1128 2 byte null */
1129 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1130 if(self_referral) {
1131 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1132 } else {
1133 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1136 /* copy in the reqpaths */
1137 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1138 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1140 offset = 8;
1141 for(i=0;i<junction->referral_count;i++) {
1142 struct referral* ref = &(junction->referral_list[i]);
1143 int unilen;
1145 SSVAL(pdata,offset,3); /* version 3 */
1146 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1147 if(self_referral) {
1148 SSVAL(pdata,offset+4,1);
1149 } else {
1150 SSVAL(pdata,offset+4,0);
1153 /* ref_flags :use path_consumed bytes? */
1154 SSVAL(pdata,offset+6,0);
1155 SIVAL(pdata,offset+8,ref->ttl);
1157 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1158 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1159 /* copy referred path into current offset */
1160 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1161 reply_size - uni_curroffset,
1162 STR_UNICODE | STR_TERMINATE);
1163 SSVAL(pdata,offset+16,uni_curroffset-offset);
1164 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1165 memset(pdata+offset+18,'\0',16);
1167 uni_curroffset += unilen;
1168 offset += VERSION3_REFERRAL_SIZE;
1170 return reply_size;
1173 /******************************************************************
1174 Set up the DFS referral for the dfs pathname. This call returns
1175 the amount of the path covered by this server, and where the
1176 client should be redirected to. This is the meat of the
1177 TRANS2_GET_DFS_REFERRAL call.
1178 ******************************************************************/
1180 int setup_dfs_referral(connection_struct *orig_conn,
1181 const char *dfs_path,
1182 int max_referral_level,
1183 char **ppdata, NTSTATUS *pstatus)
1185 struct junction_map *junction = NULL;
1186 int consumedcnt = 0;
1187 bool self_referral = False;
1188 int reply_size = 0;
1189 char *pathnamep = NULL;
1190 char *local_dfs_path = NULL;
1191 TALLOC_CTX *ctx;
1193 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1194 *pstatus = NT_STATUS_NO_MEMORY;
1195 return -1;
1198 /* get the junction entry */
1199 if (!dfs_path) {
1200 talloc_destroy(ctx);
1201 *pstatus = NT_STATUS_NOT_FOUND;
1202 return -1;
1206 * Trim pathname sent by client so it begins with only one backslash.
1207 * Two backslashes confuse some dfs clients
1210 local_dfs_path = talloc_strdup(ctx,dfs_path);
1211 if (!local_dfs_path) {
1212 *pstatus = NT_STATUS_NO_MEMORY;
1213 talloc_destroy(ctx);
1214 return -1;
1216 pathnamep = local_dfs_path;
1217 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1218 IS_DIRECTORY_SEP(pathnamep[1])) {
1219 pathnamep++;
1222 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1223 if (!junction) {
1224 *pstatus = NT_STATUS_NO_MEMORY;
1225 talloc_destroy(ctx);
1226 return -1;
1229 /* The following call can change cwd. */
1230 *pstatus = get_referred_path(ctx, pathnamep, junction,
1231 &consumedcnt, &self_referral);
1232 if (!NT_STATUS_IS_OK(*pstatus)) {
1233 vfs_ChDir(orig_conn,orig_conn->connectpath);
1234 talloc_destroy(ctx);
1235 return -1;
1237 vfs_ChDir(orig_conn,orig_conn->connectpath);
1239 if (!self_referral) {
1240 pathnamep[consumedcnt] = '\0';
1242 if( DEBUGLVL( 3 ) ) {
1243 int i=0;
1244 dbgtext("setup_dfs_referral: Path %s to "
1245 "alternate path(s):",
1246 pathnamep);
1247 for(i=0;i<junction->referral_count;i++)
1248 dbgtext(" %s",
1249 junction->referral_list[i].alternate_path);
1250 dbgtext(".\n");
1254 /* create the referral depeding on version */
1255 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1257 if (max_referral_level < 2) {
1258 max_referral_level = 2;
1260 if (max_referral_level > 3) {
1261 max_referral_level = 3;
1264 switch(max_referral_level) {
1265 case 2:
1266 reply_size = setup_ver2_dfs_referral(pathnamep,
1267 ppdata, junction,
1268 self_referral);
1269 break;
1270 case 3:
1271 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1272 junction, self_referral);
1273 break;
1274 default:
1275 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1276 "version: %d\n",
1277 max_referral_level));
1278 talloc_destroy(ctx);
1279 *pstatus = NT_STATUS_INVALID_LEVEL;
1280 return -1;
1283 if (DEBUGLVL(10)) {
1284 DEBUGADD(0,("DFS Referral pdata:\n"));
1285 dump_data(0,(uint8 *)*ppdata,reply_size);
1288 talloc_destroy(ctx);
1289 *pstatus = NT_STATUS_OK;
1290 return reply_size;
1293 /**********************************************************************
1294 The following functions are called by the NETDFS RPC pipe functions
1295 **********************************************************************/
1297 /*********************************************************************
1298 Creates a junction structure from a DFS pathname
1299 **********************************************************************/
1301 bool create_junction(TALLOC_CTX *ctx,
1302 const char *dfs_path,
1303 struct junction_map *jucn)
1305 int snum;
1306 bool dummy;
1307 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1308 NTSTATUS status;
1310 if (!pdp) {
1311 return False;
1313 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1314 if (!NT_STATUS_IS_OK(status)) {
1315 return False;
1318 /* check if path is dfs : validate first token */
1319 if (!is_myname_or_ipaddr(pdp->hostname)) {
1320 DEBUG(4,("create_junction: Invalid hostname %s "
1321 "in dfs path %s\n",
1322 pdp->hostname, dfs_path));
1323 TALLOC_FREE(pdp);
1324 return False;
1327 /* Check for a non-DFS share */
1328 snum = lp_servicenumber(pdp->servicename);
1330 if(snum < 0 || !lp_msdfs_root(snum)) {
1331 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1332 pdp->servicename));
1333 TALLOC_FREE(pdp);
1334 return False;
1337 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1338 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1339 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1341 TALLOC_FREE(pdp);
1342 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1343 return False;
1345 return True;
1348 /**********************************************************************
1349 Forms a valid Unix pathname from the junction
1350 **********************************************************************/
1352 static bool junction_to_local_path(const struct junction_map *jucn,
1353 char **pp_path_out,
1354 connection_struct **conn_out,
1355 char **oldpath)
1357 int snum;
1358 NTSTATUS status;
1360 snum = lp_servicenumber(jucn->service_name);
1361 if(snum < 0) {
1362 return False;
1364 status = create_conn_struct(talloc_tos(), conn_out, snum,
1365 lp_pathname(snum), NULL, oldpath);
1366 if (!NT_STATUS_IS_OK(status)) {
1367 return False;
1370 *pp_path_out = talloc_asprintf(*conn_out,
1371 "%s/%s",
1372 lp_pathname(snum),
1373 jucn->volume_name);
1374 if (!*pp_path_out) {
1375 vfs_ChDir(*conn_out, *oldpath);
1376 conn_free(*conn_out);
1377 return False;
1379 return True;
1382 bool create_msdfs_link(const struct junction_map *jucn)
1384 char *path = NULL;
1385 char *cwd;
1386 char *msdfs_link = NULL;
1387 connection_struct *conn;
1388 int i=0;
1389 bool insert_comma = False;
1390 bool ret = False;
1392 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1393 return False;
1396 /* Form the msdfs_link contents */
1397 msdfs_link = talloc_strdup(conn, "msdfs:");
1398 if (!msdfs_link) {
1399 goto out;
1401 for(i=0; i<jucn->referral_count; i++) {
1402 char *refpath = jucn->referral_list[i].alternate_path;
1404 /* Alternate paths always use Windows separators. */
1405 trim_char(refpath, '\\', '\\');
1406 if(*refpath == '\0') {
1407 if (i == 0) {
1408 insert_comma = False;
1410 continue;
1412 if (i > 0 && insert_comma) {
1413 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1414 ",%s",
1415 refpath);
1416 } else {
1417 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1418 "%s",
1419 refpath);
1422 if (!msdfs_link) {
1423 goto out;
1425 if (!insert_comma) {
1426 insert_comma = True;
1430 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1431 path, msdfs_link));
1433 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1434 if (errno == EEXIST) {
1435 struct smb_filename *smb_fname = NULL;
1436 NTSTATUS status;
1438 status = create_synthetic_smb_fname(talloc_tos(), path,
1439 NULL, NULL,
1440 &smb_fname);
1441 if (!NT_STATUS_IS_OK(status)) {
1442 errno = map_errno_from_nt_status(status);
1443 goto out;
1446 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1447 TALLOC_FREE(smb_fname);
1448 goto out;
1450 TALLOC_FREE(smb_fname);
1452 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1453 DEBUG(1,("create_msdfs_link: symlink failed "
1454 "%s -> %s\nError: %s\n",
1455 path, msdfs_link, strerror(errno)));
1456 goto out;
1460 ret = True;
1462 out:
1463 vfs_ChDir(conn, cwd);
1464 conn_free(conn);
1465 return ret;
1468 bool remove_msdfs_link(const struct junction_map *jucn)
1470 char *path = NULL;
1471 char *cwd;
1472 connection_struct *conn;
1473 bool ret = False;
1474 struct smb_filename *smb_fname = NULL;
1475 NTSTATUS status;
1477 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1478 return false;
1481 status = create_synthetic_smb_fname(talloc_tos(), path,
1482 NULL, NULL,
1483 &smb_fname);
1484 if (!NT_STATUS_IS_OK(status)) {
1485 errno = map_errno_from_nt_status(status);
1486 return false;
1489 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1490 ret = True;
1493 TALLOC_FREE(smb_fname);
1494 vfs_ChDir(conn, cwd);
1495 conn_free(conn);
1496 return ret;
1499 /*********************************************************************
1500 Return the number of DFS links at the root of this share.
1501 *********************************************************************/
1503 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1505 size_t cnt = 0;
1506 SMB_STRUCT_DIR *dirp = NULL;
1507 const char *dname = NULL;
1508 char *talloced = NULL;
1509 const char *connect_path = lp_pathname(snum);
1510 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1511 connection_struct *conn;
1512 NTSTATUS status;
1513 char *cwd;
1515 if(*connect_path == '\0') {
1516 return 0;
1520 * Fake up a connection struct for the VFS layer.
1523 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1524 NULL, &cwd);
1525 if (!NT_STATUS_IS_OK(status)) {
1526 DEBUG(3, ("create_conn_struct failed: %s\n",
1527 nt_errstr(status)));
1528 return 0;
1531 /* Count a link for the msdfs root - convention */
1532 cnt = 1;
1534 /* No more links if this is an msdfs proxy. */
1535 if (*msdfs_proxy != '\0') {
1536 goto out;
1539 /* Now enumerate all dfs links */
1540 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1541 if(!dirp) {
1542 goto out;
1545 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1546 != NULL) {
1547 if (is_msdfs_link(conn,
1548 dname,
1549 NULL)) {
1550 cnt++;
1552 TALLOC_FREE(talloced);
1555 SMB_VFS_CLOSEDIR(conn,dirp);
1557 out:
1558 vfs_ChDir(conn, cwd);
1559 conn_free(conn);
1560 return cnt;
1563 /*********************************************************************
1564 *********************************************************************/
1566 static int form_junctions(TALLOC_CTX *ctx,
1567 int snum,
1568 struct junction_map *jucn,
1569 size_t jn_remain)
1571 size_t cnt = 0;
1572 SMB_STRUCT_DIR *dirp = NULL;
1573 const char *dname = NULL;
1574 char *talloced = NULL;
1575 const char *connect_path = lp_pathname(snum);
1576 char *service_name = lp_servicename(snum);
1577 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1578 connection_struct *conn;
1579 struct referral *ref = NULL;
1580 char *cwd;
1581 NTSTATUS status;
1583 if (jn_remain == 0) {
1584 return 0;
1587 if(*connect_path == '\0') {
1588 return 0;
1592 * Fake up a connection struct for the VFS layer.
1595 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1596 &cwd);
1597 if (!NT_STATUS_IS_OK(status)) {
1598 DEBUG(3, ("create_conn_struct failed: %s\n",
1599 nt_errstr(status)));
1600 return 0;
1603 /* form a junction for the msdfs root - convention
1604 DO NOT REMOVE THIS: NT clients will not work with us
1605 if this is not present
1607 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1608 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1609 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1610 goto out;
1612 jucn[cnt].comment = "";
1613 jucn[cnt].referral_count = 1;
1615 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1616 if (jucn[cnt].referral_list == NULL) {
1617 goto out;
1620 ref->proximity = 0;
1621 ref->ttl = REFERRAL_TTL;
1622 if (*msdfs_proxy != '\0') {
1623 ref->alternate_path = talloc_strdup(ctx,
1624 msdfs_proxy);
1625 } else {
1626 ref->alternate_path = talloc_asprintf(ctx,
1627 "\\\\%s\\%s",
1628 get_local_machine_name(),
1629 service_name);
1632 if (!ref->alternate_path) {
1633 goto out;
1635 cnt++;
1637 /* Don't enumerate if we're an msdfs proxy. */
1638 if (*msdfs_proxy != '\0') {
1639 goto out;
1642 /* Now enumerate all dfs links */
1643 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1644 if(!dirp) {
1645 goto out;
1648 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1649 != NULL) {
1650 char *link_target = NULL;
1651 if (cnt >= jn_remain) {
1652 DEBUG(2, ("form_junctions: ran out of MSDFS "
1653 "junction slots"));
1654 TALLOC_FREE(talloced);
1655 goto out;
1657 if (is_msdfs_link_internal(ctx,
1658 conn,
1659 dname, &link_target,
1660 NULL)) {
1661 if (parse_msdfs_symlink(ctx,
1662 link_target,
1663 &jucn[cnt].referral_list,
1664 &jucn[cnt].referral_count)) {
1666 jucn[cnt].service_name = talloc_strdup(ctx,
1667 service_name);
1668 jucn[cnt].volume_name = talloc_strdup(ctx,
1669 dname);
1670 if (!jucn[cnt].service_name ||
1671 !jucn[cnt].volume_name) {
1672 TALLOC_FREE(talloced);
1673 goto out;
1675 jucn[cnt].comment = "";
1676 cnt++;
1678 TALLOC_FREE(link_target);
1680 TALLOC_FREE(talloced);
1683 out:
1685 if (dirp) {
1686 SMB_VFS_CLOSEDIR(conn,dirp);
1689 vfs_ChDir(conn, cwd);
1690 conn_free(conn);
1691 return cnt;
1694 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1696 struct junction_map *jn = NULL;
1697 int i=0;
1698 size_t jn_count = 0;
1699 int sharecount = 0;
1701 *p_num_jn = 0;
1702 if(!lp_host_msdfs()) {
1703 return NULL;
1706 /* Ensure all the usershares are loaded. */
1707 become_root();
1708 load_registry_shares();
1709 sharecount = load_usershare_shares();
1710 unbecome_root();
1712 for(i=0;i < sharecount;i++) {
1713 if(lp_msdfs_root(i)) {
1714 jn_count += count_dfs_links(ctx, i);
1717 if (jn_count == 0) {
1718 return NULL;
1720 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1721 if (!jn) {
1722 return NULL;
1724 for(i=0; i < sharecount; i++) {
1725 if (*p_num_jn >= jn_count) {
1726 break;
1728 if(lp_msdfs_root(i)) {
1729 *p_num_jn += form_junctions(ctx, i,
1730 &jn[*p_num_jn],
1731 jn_count - *p_num_jn);
1734 return jn;
1737 /******************************************************************************
1738 Core function to resolve a dfs pathname possibly containing a wildcard. If
1739 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1740 detected during dfs resolution.
1741 ******************************************************************************/
1743 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1744 connection_struct *conn,
1745 bool dfs_pathnames,
1746 const char *name_in,
1747 bool allow_wcards,
1748 char **pp_name_out,
1749 bool *ppath_contains_wcard)
1751 bool path_contains_wcard;
1752 NTSTATUS status = NT_STATUS_OK;
1754 if (dfs_pathnames) {
1755 status = dfs_redirect(ctx,
1756 conn,
1757 name_in,
1758 allow_wcards,
1759 pp_name_out,
1760 &path_contains_wcard);
1762 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1763 *ppath_contains_wcard = path_contains_wcard;
1765 } else {
1767 * Cheat and just return a copy of the in ptr.
1768 * Once srvstr_get_path() uses talloc it'll
1769 * be a talloced ptr anyway.
1771 *pp_name_out = CONST_DISCARD(char *,name_in);
1773 return status;