s3: Remove unused args from nss_get_info_cached
[Samba.git] / source3 / smbd / msdfs.c
blobc11e66e3b8713d387fe49e38ec7244f400ba5e1c
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 performs a vfs connect and 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 *session_info,
223 char **poldcwd)
225 connection_struct *conn;
226 char *connpath;
227 char *oldcwd;
228 const char *vfs_user;
230 conn = TALLOC_ZERO_P(ctx, connection_struct);
231 if (conn == NULL) {
232 return NT_STATUS_NO_MEMORY;
235 connpath = talloc_strdup(conn, path);
236 if (!connpath) {
237 TALLOC_FREE(conn);
238 return NT_STATUS_NO_MEMORY;
240 connpath = talloc_string_sub(conn,
241 connpath,
242 "%S",
243 lp_servicename(snum));
244 if (!connpath) {
245 TALLOC_FREE(conn);
246 return NT_STATUS_NO_MEMORY;
249 /* needed for smbd_vfs_init() */
251 if (!(conn->params = TALLOC_ZERO_P(conn, struct share_params))) {
252 DEBUG(0, ("TALLOC failed\n"));
253 TALLOC_FREE(conn);
254 return NT_STATUS_NO_MEMORY;
257 conn->params->service = snum;
259 conn->sconn = smbd_server_conn;
260 conn->sconn->num_tcons_open++;
262 if (session_info != NULL) {
263 conn->session_info = copy_serverinfo(conn, session_info);
264 if (conn->session_info == NULL) {
265 DEBUG(0, ("copy_serverinfo failed\n"));
266 TALLOC_FREE(conn);
267 return NT_STATUS_NO_MEMORY;
269 vfs_user = conn->session_info->unix_name;
270 } else {
271 /* use current authenticated user in absence of session_info */
272 vfs_user = get_current_username();
275 set_conn_connectpath(conn, connpath);
277 if (!smbd_vfs_init(conn)) {
278 NTSTATUS status = map_nt_error_from_unix(errno);
279 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
280 conn_free(conn);
281 return status;
284 /* this must be the first filesystem operation that we do */
285 if (SMB_VFS_CONNECT(conn, lp_servicename(snum), vfs_user) < 0) {
286 DEBUG(0,("VFS connect failed!\n"));
287 conn_free(conn);
288 return NT_STATUS_UNSUCCESSFUL;
291 conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res);
294 * Windows seems to insist on doing trans2getdfsreferral() calls on
295 * the IPC$ share as the anonymous user. If we try to chdir as that
296 * user we will fail.... WTF ? JRA.
299 oldcwd = vfs_GetWd(ctx, conn);
300 if (oldcwd == NULL) {
301 NTSTATUS status = map_nt_error_from_unix(errno);
302 DEBUG(3, ("vfs_GetWd failed: %s\n", strerror(errno)));
303 conn_free(conn);
304 return status;
307 if (vfs_ChDir(conn,conn->connectpath) != 0) {
308 NTSTATUS status = map_nt_error_from_unix(errno);
309 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. "
310 "Error was %s\n",
311 conn->connectpath, strerror(errno) ));
312 conn_free(conn);
313 return status;
316 *pconn = conn;
317 *poldcwd = oldcwd;
319 return NT_STATUS_OK;
322 /**********************************************************************
323 Parse the contents of a symlink to verify if it is an msdfs referral
324 A valid referral is of the form:
326 msdfs:server1\share1,server2\share2
327 msdfs:server1\share1\pathname,server2\share2\pathname
328 msdfs:server1/share1,server2/share2
329 msdfs:server1/share1/pathname,server2/share2/pathname.
331 Note that the alternate paths returned here must be of the canonicalized
332 form:
334 \server\share or
335 \server\share\path\to\file,
337 even in posix path mode. This is because we have no knowledge if the
338 server we're referring to understands posix paths.
339 **********************************************************************/
341 static bool parse_msdfs_symlink(TALLOC_CTX *ctx,
342 const char *target,
343 struct referral **preflist,
344 int *refcount)
346 char *temp = NULL;
347 char *prot;
348 char **alt_path = NULL;
349 int count = 0, i;
350 struct referral *reflist;
351 char *saveptr;
353 temp = talloc_strdup(ctx, target);
354 if (!temp) {
355 return False;
357 prot = strtok_r(temp, ":", &saveptr);
358 if (!prot) {
359 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
360 return False;
363 alt_path = TALLOC_ARRAY(ctx, char *, MAX_REFERRAL_COUNT);
364 if (!alt_path) {
365 return False;
368 /* parse out the alternate paths */
369 while((count<MAX_REFERRAL_COUNT) &&
370 ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) {
371 count++;
374 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
376 if (count) {
377 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx,
378 struct referral, count);
379 if(reflist == NULL) {
380 TALLOC_FREE(alt_path);
381 return False;
383 } else {
384 reflist = *preflist = NULL;
387 for(i=0;i<count;i++) {
388 char *p;
390 /* Canonicalize link target.
391 * Replace all /'s in the path by a \ */
392 string_replace(alt_path[i], '/', '\\');
394 /* Remove leading '\\'s */
395 p = alt_path[i];
396 while (*p && (*p == '\\')) {
397 p++;
400 reflist[i].alternate_path = talloc_asprintf(ctx,
401 "\\%s",
403 if (!reflist[i].alternate_path) {
404 return False;
407 reflist[i].proximity = 0;
408 reflist[i].ttl = REFERRAL_TTL;
409 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n",
410 reflist[i].alternate_path));
413 *refcount = count;
415 TALLOC_FREE(alt_path);
416 return True;
419 /**********************************************************************
420 Returns true if the unix path is a valid msdfs symlink and also
421 returns the target string from inside the link.
422 **********************************************************************/
424 static bool is_msdfs_link_internal(TALLOC_CTX *ctx,
425 connection_struct *conn,
426 const char *path,
427 char **pp_link_target,
428 SMB_STRUCT_STAT *sbufp)
430 int referral_len = 0;
431 #if defined(HAVE_BROKEN_READLINK)
432 char link_target_buf[PATH_MAX];
433 #else
434 char link_target_buf[7];
435 #endif
436 size_t bufsize = 0;
437 char *link_target = NULL;
438 struct smb_filename smb_fname;
440 if (pp_link_target) {
441 bufsize = 1024;
442 link_target = TALLOC_ARRAY(ctx, char, bufsize);
443 if (!link_target) {
444 return False;
446 *pp_link_target = link_target;
447 } else {
448 bufsize = sizeof(link_target_buf);
449 link_target = link_target_buf;
452 ZERO_STRUCT(smb_fname);
453 smb_fname.base_name = discard_const_p(char, path);
455 if (SMB_VFS_LSTAT(conn, &smb_fname) != 0) {
456 DEBUG(5,("is_msdfs_link_read_target: %s does not exist.\n",
457 path));
458 goto err;
460 if (!S_ISLNK(smb_fname.st.st_ex_mode)) {
461 DEBUG(5,("is_msdfs_link_read_target: %s is not a link.\n",
462 path));
463 goto err;
465 if (sbufp != NULL) {
466 *sbufp = smb_fname.st;
469 referral_len = SMB_VFS_READLINK(conn, path, link_target, bufsize - 1);
470 if (referral_len == -1) {
471 DEBUG(0,("is_msdfs_link_read_target: Error reading "
472 "msdfs link %s: %s\n",
473 path, strerror(errno)));
474 goto err;
476 link_target[referral_len] = '\0';
478 DEBUG(5,("is_msdfs_link_internal: %s -> %s\n",path,
479 link_target));
481 if (!strnequal(link_target, "msdfs:", 6)) {
482 goto err;
484 return True;
486 err:
488 if (link_target != link_target_buf) {
489 TALLOC_FREE(link_target);
491 return False;
494 /**********************************************************************
495 Returns true if the unix path is a valid msdfs symlink.
496 **********************************************************************/
498 bool is_msdfs_link(connection_struct *conn,
499 const char *path,
500 SMB_STRUCT_STAT *sbufp)
502 return is_msdfs_link_internal(talloc_tos(),
503 conn,
504 path,
505 NULL,
506 sbufp);
509 /*****************************************************************
510 Used by other functions to decide if a dfs path is remote,
511 and to get the list of referred locations for that remote path.
513 search_flag: For findfirsts, dfs links themselves are not
514 redirected, but paths beyond the links are. For normal smb calls,
515 even dfs links need to be redirected.
517 consumedcntp: how much of the dfs path is being redirected. the client
518 should try the remaining path on the redirected server.
520 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
521 link redirect are in targetpath.
522 *****************************************************************/
524 static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
525 connection_struct *conn,
526 const char *dfspath, /* Incoming complete dfs path */
527 const struct dfs_path *pdp, /* Parsed out
528 server+share+extrapath. */
529 bool search_flag, /* Called from a findfirst ? */
530 int *consumedcntp,
531 char **pp_targetpath)
533 char *p = NULL;
534 char *q = NULL;
535 NTSTATUS status;
536 struct smb_filename *smb_fname = NULL;
537 char *canon_dfspath = NULL; /* Canonicalized dfs path. (only '/'
538 components). */
540 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
541 conn->connectpath, pdp->reqpath));
544 * Note the unix path conversion here we're doing we can
545 * throw away. We're looking for a symlink for a dfs
546 * resolution, if we don't find it we'll do another
547 * unix_convert later in the codepath.
548 * If we needed to remember what we'd resolved in
549 * dp->reqpath (as the original code did) we'd
550 * copy (localhost, dp->reqpath) on any code
551 * path below that returns True - but I don't
552 * think this is needed. JRA.
555 status = unix_convert(ctx, conn, pdp->reqpath, &smb_fname,
556 search_flag ? UCF_ALWAYS_ALLOW_WCARD_LCOMP : 0);
558 if (!NT_STATUS_IS_OK(status)) {
559 if (!NT_STATUS_EQUAL(status,
560 NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
561 return status;
564 /* Create an smb_fname to use below. */
565 status = create_synthetic_smb_fname(ctx, pdp->reqpath, NULL,
566 NULL, &smb_fname);
567 if (!NT_STATUS_IS_OK(status)) {
568 return status;
572 /* Optimization - check if we can redirect the whole path. */
574 if (is_msdfs_link_internal(ctx, conn, smb_fname->base_name,
575 pp_targetpath, NULL)) {
576 if (search_flag) {
577 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
578 "for dfs link %s.\n", dfspath));
579 status = NT_STATUS_OK;
580 goto out;
583 DEBUG(6,("dfs_path_lookup: %s resolves to a "
584 "valid dfs link %s.\n", dfspath,
585 pp_targetpath ? *pp_targetpath : ""));
587 if (consumedcntp) {
588 *consumedcntp = strlen(dfspath);
590 status = NT_STATUS_PATH_NOT_COVERED;
591 goto out;
594 /* Prepare to test only for '/' components in the given path,
595 * so if a Windows path replace all '\\' characters with '/'.
596 * For a POSIX DFS path we know all separators are already '/'. */
598 canon_dfspath = talloc_strdup(ctx, dfspath);
599 if (!canon_dfspath) {
600 status = NT_STATUS_NO_MEMORY;
601 goto out;
603 if (!pdp->posix_path) {
604 string_replace(canon_dfspath, '\\', '/');
608 * localpath comes out of unix_convert, so it has
609 * no trailing backslash. Make sure that canon_dfspath hasn't either.
610 * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
613 trim_char(canon_dfspath,0,'/');
616 * Redirect if any component in the path is a link.
617 * We do this by walking backwards through the
618 * local path, chopping off the last component
619 * in both the local path and the canonicalized
620 * DFS path. If we hit a DFS link then we're done.
623 p = strrchr_m(smb_fname->base_name, '/');
624 if (consumedcntp) {
625 q = strrchr_m(canon_dfspath, '/');
628 while (p) {
629 *p = '\0';
630 if (q) {
631 *q = '\0';
634 if (is_msdfs_link_internal(ctx, conn,
635 smb_fname->base_name, pp_targetpath,
636 NULL)) {
637 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
638 "parent %s is dfs link\n", dfspath,
639 smb_fname_str_dbg(smb_fname)));
641 if (consumedcntp) {
642 *consumedcntp = strlen(canon_dfspath);
643 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
644 "(%d)\n",
645 canon_dfspath,
646 *consumedcntp));
649 status = NT_STATUS_PATH_NOT_COVERED;
650 goto out;
653 /* Step back on the filesystem. */
654 p = strrchr_m(smb_fname->base_name, '/');
656 if (consumedcntp) {
657 /* And in the canonicalized dfs path. */
658 q = strrchr_m(canon_dfspath, '/');
662 status = NT_STATUS_OK;
663 out:
664 TALLOC_FREE(smb_fname);
665 return status;
668 /*****************************************************************
669 Decides if a dfs pathname should be redirected or not.
670 If not, the pathname is converted to a tcon-relative local unix path
672 search_wcard_flag: this flag performs 2 functions both related
673 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
674 for details.
676 This function can return NT_STATUS_OK, meaning use the returned path as-is
677 (mapped into a local path).
678 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
679 any other NT_STATUS error which is a genuine error to be
680 returned to the client.
681 *****************************************************************/
683 static NTSTATUS dfs_redirect(TALLOC_CTX *ctx,
684 connection_struct *conn,
685 const char *path_in,
686 bool search_wcard_flag,
687 char **pp_path_out,
688 bool *ppath_contains_wcard)
690 NTSTATUS status;
691 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
693 if (!pdp) {
694 return NT_STATUS_NO_MEMORY;
697 status = parse_dfs_path(conn, path_in, search_wcard_flag, pdp,
698 ppath_contains_wcard);
699 if (!NT_STATUS_IS_OK(status)) {
700 TALLOC_FREE(pdp);
701 return status;
704 if (pdp->reqpath[0] == '\0') {
705 TALLOC_FREE(pdp);
706 *pp_path_out = talloc_strdup(ctx, "");
707 if (!*pp_path_out) {
708 return NT_STATUS_NO_MEMORY;
710 DEBUG(5,("dfs_redirect: self-referral.\n"));
711 return NT_STATUS_OK;
714 /* If dfs pathname for a non-dfs share, convert to tcon-relative
715 path and return OK */
717 if (!lp_msdfs_root(SNUM(conn))) {
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 it looked like a local path (zero hostname/servicename)
727 * just treat as a tcon-relative path. */
729 if (pdp->hostname[0] == '\0' && pdp->servicename[0] == '\0') {
730 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
731 TALLOC_FREE(pdp);
732 if (!*pp_path_out) {
733 return NT_STATUS_NO_MEMORY;
735 return NT_STATUS_OK;
738 if (!( strequal(pdp->servicename, lp_servicename(SNUM(conn)))
739 || (strequal(pdp->servicename, HOMES_NAME)
740 && strequal(lp_servicename(SNUM(conn)),
741 conn->session_info->sanitized_username) )) ) {
743 /* The given sharename doesn't match this connection. */
744 TALLOC_FREE(pdp);
746 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
749 status = dfs_path_lookup(ctx, conn, path_in, pdp,
750 search_wcard_flag, NULL, NULL);
751 if (!NT_STATUS_IS_OK(status)) {
752 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
753 DEBUG(3,("dfs_redirect: Redirecting %s\n", path_in));
754 } else {
755 DEBUG(10,("dfs_redirect: dfs_path_lookup "
756 "failed for %s with %s\n",
757 path_in, nt_errstr(status) ));
759 return status;
762 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", path_in));
764 /* Form non-dfs tcon-relative path */
765 *pp_path_out = talloc_strdup(ctx, pdp->reqpath);
766 TALLOC_FREE(pdp);
767 if (!*pp_path_out) {
768 return NT_STATUS_NO_MEMORY;
771 DEBUG(3,("dfs_redirect: Path %s converted to non-dfs path %s\n",
772 path_in,
773 *pp_path_out));
775 return NT_STATUS_OK;
778 /**********************************************************************
779 Return a self referral.
780 **********************************************************************/
782 static NTSTATUS self_ref(TALLOC_CTX *ctx,
783 const char *dfs_path,
784 struct junction_map *jucn,
785 int *consumedcntp,
786 bool *self_referralp)
788 struct referral *ref;
790 *self_referralp = True;
792 jucn->referral_count = 1;
793 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
794 return NT_STATUS_NO_MEMORY;
797 ref->alternate_path = talloc_strdup(ctx, dfs_path);
798 if (!ref->alternate_path) {
799 return NT_STATUS_NO_MEMORY;
801 ref->proximity = 0;
802 ref->ttl = REFERRAL_TTL;
803 jucn->referral_list = ref;
804 *consumedcntp = strlen(dfs_path);
805 return NT_STATUS_OK;
808 /**********************************************************************
809 Gets valid referrals for a dfs path and fills up the
810 junction_map structure.
811 **********************************************************************/
813 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
814 const char *dfs_path,
815 struct junction_map *jucn,
816 int *consumedcntp,
817 bool *self_referralp)
819 struct connection_struct *conn;
820 char *targetpath = NULL;
821 int snum;
822 NTSTATUS status = NT_STATUS_NOT_FOUND;
823 bool dummy;
824 struct dfs_path *pdp = TALLOC_P(ctx, struct dfs_path);
825 char *oldpath;
827 if (!pdp) {
828 return NT_STATUS_NO_MEMORY;
831 *self_referralp = False;
833 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
834 if (!NT_STATUS_IS_OK(status)) {
835 return status;
838 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
839 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
840 if (!jucn->service_name || !jucn->volume_name) {
841 TALLOC_FREE(pdp);
842 return NT_STATUS_NO_MEMORY;
845 /* Verify the share is a dfs root */
846 snum = lp_servicenumber(jucn->service_name);
847 if(snum < 0) {
848 char *service_name = NULL;
849 if ((snum = find_service(ctx, jucn->service_name, &service_name)) < 0) {
850 return NT_STATUS_NOT_FOUND;
852 if (!service_name) {
853 return NT_STATUS_NO_MEMORY;
855 TALLOC_FREE(jucn->service_name);
856 jucn->service_name = talloc_strdup(ctx, service_name);
857 if (!jucn->service_name) {
858 TALLOC_FREE(pdp);
859 return NT_STATUS_NO_MEMORY;
863 if (!lp_msdfs_root(snum) && (*lp_msdfs_proxy(snum) == '\0')) {
864 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not "
865 "a dfs root.\n",
866 pdp->servicename, dfs_path));
867 TALLOC_FREE(pdp);
868 return NT_STATUS_NOT_FOUND;
872 * Self referrals are tested with a anonymous IPC connection and
873 * a GET_DFS_REFERRAL call to \\server\share. (which means
874 * dp.reqpath[0] points to an empty string). create_conn_struct cd's
875 * into the directory and will fail if it cannot (as the anonymous
876 * user). Cope with this.
879 if (pdp->reqpath[0] == '\0') {
880 char *tmp;
881 struct referral *ref;
883 if (*lp_msdfs_proxy(snum) == '\0') {
884 TALLOC_FREE(pdp);
885 return self_ref(ctx,
886 dfs_path,
887 jucn,
888 consumedcntp,
889 self_referralp);
893 * It's an msdfs proxy share. Redirect to
894 * the configured target share.
897 jucn->referral_count = 1;
898 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
899 TALLOC_FREE(pdp);
900 return NT_STATUS_NO_MEMORY;
903 if (!(tmp = talloc_strdup(ctx, lp_msdfs_proxy(snum)))) {
904 TALLOC_FREE(pdp);
905 return NT_STATUS_NO_MEMORY;
908 trim_string(tmp, "\\", 0);
910 ref->alternate_path = talloc_asprintf(ctx, "\\%s", tmp);
911 TALLOC_FREE(tmp);
913 if (!ref->alternate_path) {
914 TALLOC_FREE(pdp);
915 return NT_STATUS_NO_MEMORY;
918 if (pdp->reqpath[0] != '\0') {
919 ref->alternate_path = talloc_asprintf_append(
920 ref->alternate_path,
921 "%s",
922 pdp->reqpath);
923 if (!ref->alternate_path) {
924 TALLOC_FREE(pdp);
925 return NT_STATUS_NO_MEMORY;
928 ref->proximity = 0;
929 ref->ttl = REFERRAL_TTL;
930 jucn->referral_list = ref;
931 *consumedcntp = strlen(dfs_path);
932 TALLOC_FREE(pdp);
933 return NT_STATUS_OK;
936 status = create_conn_struct(ctx, &conn, snum, lp_pathname(snum),
937 NULL, &oldpath);
938 if (!NT_STATUS_IS_OK(status)) {
939 TALLOC_FREE(pdp);
940 return status;
943 /* If this is a DFS path dfs_lookup should return
944 * NT_STATUS_PATH_NOT_COVERED. */
946 status = dfs_path_lookup(ctx, conn, dfs_path, pdp,
947 False, consumedcntp, &targetpath);
949 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
950 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
951 dfs_path));
952 goto err_exit;
955 /* We know this is a valid dfs link. Parse the targetpath. */
956 if (!parse_msdfs_symlink(ctx, targetpath,
957 &jucn->referral_list,
958 &jucn->referral_count)) {
959 DEBUG(3,("get_referred_path: failed to parse symlink "
960 "target %s\n", targetpath ));
961 status = NT_STATUS_NOT_FOUND;
962 goto err_exit;
965 status = NT_STATUS_OK;
966 err_exit:
967 vfs_ChDir(conn, oldpath);
968 SMB_VFS_DISCONNECT(conn);
969 conn_free(conn);
970 TALLOC_FREE(pdp);
971 return status;
974 static int setup_ver2_dfs_referral(const char *pathname,
975 char **ppdata,
976 struct junction_map *junction,
977 bool self_referral)
979 char* pdata = *ppdata;
981 smb_ucs2_t *uni_requestedpath = NULL;
982 int uni_reqpathoffset1,uni_reqpathoffset2;
983 int uni_curroffset;
984 int requestedpathlen=0;
985 int offset;
986 int reply_size = 0;
987 int i=0;
989 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
991 requestedpathlen = rpcstr_push_talloc(talloc_tos(),
992 &uni_requestedpath, pathname);
993 if (uni_requestedpath == NULL || requestedpathlen == 0) {
994 return -1;
997 if (DEBUGLVL(10)) {
998 dump_data(0, (unsigned char *)uni_requestedpath,
999 requestedpathlen);
1002 DEBUG(10,("ref count = %u\n",junction->referral_count));
1004 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1005 VERSION2_REFERRAL_SIZE * junction->referral_count;
1007 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
1009 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
1011 reply_size = REFERRAL_HEADER_SIZE +
1012 VERSION2_REFERRAL_SIZE*junction->referral_count +
1013 2 * requestedpathlen;
1014 DEBUG(10,("reply_size: %u\n",reply_size));
1016 /* add up the unicode lengths of all the referral paths */
1017 for(i=0;i<junction->referral_count;i++) {
1018 DEBUG(10,("referral %u : %s\n",
1020 junction->referral_list[i].alternate_path));
1021 reply_size +=
1022 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1025 DEBUG(10,("reply_size = %u\n",reply_size));
1026 /* add the unexplained 0x16 bytes */
1027 reply_size += 0x16;
1029 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1030 if(pdata == NULL) {
1031 DEBUG(0,("Realloc failed!\n"));
1032 return -1;
1034 *ppdata = pdata;
1036 /* copy in the dfs requested paths.. required for offset calculations */
1037 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
1038 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
1040 /* create the header */
1041 SSVAL(pdata,0,requestedpathlen - 2); /* UCS2 of path consumed minus
1042 2 byte null */
1043 /* number of referral in this pkt */
1044 SSVAL(pdata,2,junction->referral_count);
1045 if(self_referral) {
1046 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1047 } else {
1048 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1051 offset = 8;
1052 /* add the referral elements */
1053 for(i=0;i<junction->referral_count;i++) {
1054 struct referral* ref = &junction->referral_list[i];
1055 int unilen;
1057 SSVAL(pdata,offset,2); /* version 2 */
1058 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
1059 if(self_referral) {
1060 SSVAL(pdata,offset+4,1);
1061 } else {
1062 SSVAL(pdata,offset+4,0);
1065 /* ref_flags :use path_consumed bytes? */
1066 SSVAL(pdata,offset+6,0);
1067 SIVAL(pdata,offset+8,ref->proximity);
1068 SIVAL(pdata,offset+12,ref->ttl);
1070 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
1071 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
1072 /* copy referred path into current offset */
1073 unilen = rpcstr_push(pdata+uni_curroffset,
1074 ref->alternate_path,
1075 reply_size - uni_curroffset,
1076 STR_UNICODE);
1078 SSVAL(pdata,offset+20,uni_curroffset-offset);
1080 uni_curroffset += unilen;
1081 offset += VERSION2_REFERRAL_SIZE;
1083 /* add in the unexplained 22 (0x16) bytes at the end */
1084 memset(pdata+uni_curroffset,'\0',0x16);
1085 return reply_size;
1088 static int setup_ver3_dfs_referral(const char *pathname,
1089 char **ppdata,
1090 struct junction_map *junction,
1091 bool self_referral)
1093 char *pdata = *ppdata;
1095 smb_ucs2_t *uni_reqpath = NULL;
1096 int uni_reqpathoffset1, uni_reqpathoffset2;
1097 int uni_curroffset;
1098 int reply_size = 0;
1100 int reqpathlen = 0;
1101 int offset,i=0;
1103 DEBUG(10,("setting up version3 referral\n"));
1105 reqpathlen = rpcstr_push_talloc(talloc_tos(), &uni_reqpath, pathname);
1106 if (uni_reqpath == NULL || reqpathlen == 0) {
1107 return -1;
1110 if (DEBUGLVL(10)) {
1111 dump_data(0, (unsigned char *)uni_reqpath,
1112 reqpathlen);
1115 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
1116 VERSION3_REFERRAL_SIZE * junction->referral_count;
1117 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
1118 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
1120 for(i=0;i<junction->referral_count;i++) {
1121 DEBUG(10,("referral %u : %s\n",
1123 junction->referral_list[i].alternate_path));
1124 reply_size +=
1125 (strlen(junction->referral_list[i].alternate_path)+1)*2;
1128 pdata = (char *)SMB_REALLOC(pdata,reply_size);
1129 if(pdata == NULL) {
1130 DEBUG(0,("version3 referral setup:"
1131 "malloc failed for Realloc!\n"));
1132 return -1;
1134 *ppdata = pdata;
1136 /* create the header */
1137 SSVAL(pdata,0,reqpathlen - 2); /* UCS2 of path consumed minus
1138 2 byte null */
1139 SSVAL(pdata,2,junction->referral_count); /* number of referral */
1140 if(self_referral) {
1141 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
1142 } else {
1143 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
1146 /* copy in the reqpaths */
1147 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
1148 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
1150 offset = 8;
1151 for(i=0;i<junction->referral_count;i++) {
1152 struct referral* ref = &(junction->referral_list[i]);
1153 int unilen;
1155 SSVAL(pdata,offset,3); /* version 3 */
1156 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
1157 if(self_referral) {
1158 SSVAL(pdata,offset+4,1);
1159 } else {
1160 SSVAL(pdata,offset+4,0);
1163 /* ref_flags :use path_consumed bytes? */
1164 SSVAL(pdata,offset+6,0);
1165 SIVAL(pdata,offset+8,ref->ttl);
1167 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
1168 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
1169 /* copy referred path into current offset */
1170 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
1171 reply_size - uni_curroffset,
1172 STR_UNICODE | STR_TERMINATE);
1173 SSVAL(pdata,offset+16,uni_curroffset-offset);
1174 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
1175 memset(pdata+offset+18,'\0',16);
1177 uni_curroffset += unilen;
1178 offset += VERSION3_REFERRAL_SIZE;
1180 return reply_size;
1183 /******************************************************************
1184 Set up the DFS referral for the dfs pathname. This call returns
1185 the amount of the path covered by this server, and where the
1186 client should be redirected to. This is the meat of the
1187 TRANS2_GET_DFS_REFERRAL call.
1188 ******************************************************************/
1190 int setup_dfs_referral(connection_struct *orig_conn,
1191 const char *dfs_path,
1192 int max_referral_level,
1193 char **ppdata, NTSTATUS *pstatus)
1195 struct junction_map *junction = NULL;
1196 int consumedcnt = 0;
1197 bool self_referral = False;
1198 int reply_size = 0;
1199 char *pathnamep = NULL;
1200 char *local_dfs_path = NULL;
1201 TALLOC_CTX *ctx;
1203 if (!(ctx=talloc_init("setup_dfs_referral"))) {
1204 *pstatus = NT_STATUS_NO_MEMORY;
1205 return -1;
1208 /* get the junction entry */
1209 if (!dfs_path) {
1210 talloc_destroy(ctx);
1211 *pstatus = NT_STATUS_NOT_FOUND;
1212 return -1;
1216 * Trim pathname sent by client so it begins with only one backslash.
1217 * Two backslashes confuse some dfs clients
1220 local_dfs_path = talloc_strdup(ctx,dfs_path);
1221 if (!local_dfs_path) {
1222 *pstatus = NT_STATUS_NO_MEMORY;
1223 talloc_destroy(ctx);
1224 return -1;
1226 pathnamep = local_dfs_path;
1227 while (IS_DIRECTORY_SEP(pathnamep[0]) &&
1228 IS_DIRECTORY_SEP(pathnamep[1])) {
1229 pathnamep++;
1232 junction = TALLOC_ZERO_P(ctx, struct junction_map);
1233 if (!junction) {
1234 *pstatus = NT_STATUS_NO_MEMORY;
1235 talloc_destroy(ctx);
1236 return -1;
1239 /* The following call can change cwd. */
1240 *pstatus = get_referred_path(ctx, pathnamep, junction,
1241 &consumedcnt, &self_referral);
1242 if (!NT_STATUS_IS_OK(*pstatus)) {
1243 vfs_ChDir(orig_conn,orig_conn->connectpath);
1244 talloc_destroy(ctx);
1245 return -1;
1247 vfs_ChDir(orig_conn,orig_conn->connectpath);
1249 if (!self_referral) {
1250 pathnamep[consumedcnt] = '\0';
1252 if( DEBUGLVL( 3 ) ) {
1253 int i=0;
1254 dbgtext("setup_dfs_referral: Path %s to "
1255 "alternate path(s):",
1256 pathnamep);
1257 for(i=0;i<junction->referral_count;i++)
1258 dbgtext(" %s",
1259 junction->referral_list[i].alternate_path);
1260 dbgtext(".\n");
1264 /* create the referral depeding on version */
1265 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
1267 if (max_referral_level < 2) {
1268 max_referral_level = 2;
1270 if (max_referral_level > 3) {
1271 max_referral_level = 3;
1274 switch(max_referral_level) {
1275 case 2:
1276 reply_size = setup_ver2_dfs_referral(pathnamep,
1277 ppdata, junction,
1278 self_referral);
1279 break;
1280 case 3:
1281 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata,
1282 junction, self_referral);
1283 break;
1284 default:
1285 DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
1286 "version: %d\n",
1287 max_referral_level));
1288 talloc_destroy(ctx);
1289 *pstatus = NT_STATUS_INVALID_LEVEL;
1290 return -1;
1293 if (DEBUGLVL(10)) {
1294 DEBUGADD(0,("DFS Referral pdata:\n"));
1295 dump_data(0,(uint8 *)*ppdata,reply_size);
1298 talloc_destroy(ctx);
1299 *pstatus = NT_STATUS_OK;
1300 return reply_size;
1303 /**********************************************************************
1304 The following functions are called by the NETDFS RPC pipe functions
1305 **********************************************************************/
1307 /*********************************************************************
1308 Creates a junction structure from a DFS pathname
1309 **********************************************************************/
1311 bool create_junction(TALLOC_CTX *ctx,
1312 const char *dfs_path,
1313 struct junction_map *jucn)
1315 int snum;
1316 bool dummy;
1317 struct dfs_path *pdp = TALLOC_P(ctx,struct dfs_path);
1318 NTSTATUS status;
1320 if (!pdp) {
1321 return False;
1323 status = parse_dfs_path(NULL, dfs_path, False, pdp, &dummy);
1324 if (!NT_STATUS_IS_OK(status)) {
1325 return False;
1328 /* check if path is dfs : validate first token */
1329 if (!is_myname_or_ipaddr(pdp->hostname)) {
1330 DEBUG(4,("create_junction: Invalid hostname %s "
1331 "in dfs path %s\n",
1332 pdp->hostname, dfs_path));
1333 TALLOC_FREE(pdp);
1334 return False;
1337 /* Check for a non-DFS share */
1338 snum = lp_servicenumber(pdp->servicename);
1340 if(snum < 0 || !lp_msdfs_root(snum)) {
1341 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1342 pdp->servicename));
1343 TALLOC_FREE(pdp);
1344 return False;
1347 jucn->service_name = talloc_strdup(ctx, pdp->servicename);
1348 jucn->volume_name = talloc_strdup(ctx, pdp->reqpath);
1349 jucn->comment = talloc_strdup(ctx, lp_comment(snum));
1351 TALLOC_FREE(pdp);
1352 if (!jucn->service_name || !jucn->volume_name || ! jucn->comment) {
1353 return False;
1355 return True;
1358 /**********************************************************************
1359 Forms a valid Unix pathname from the junction
1360 **********************************************************************/
1362 static bool junction_to_local_path(const struct junction_map *jucn,
1363 char **pp_path_out,
1364 connection_struct **conn_out,
1365 char **oldpath)
1367 int snum;
1368 NTSTATUS status;
1370 snum = lp_servicenumber(jucn->service_name);
1371 if(snum < 0) {
1372 return False;
1374 status = create_conn_struct(talloc_tos(), conn_out, snum,
1375 lp_pathname(snum), NULL, oldpath);
1376 if (!NT_STATUS_IS_OK(status)) {
1377 return False;
1380 *pp_path_out = talloc_asprintf(*conn_out,
1381 "%s/%s",
1382 lp_pathname(snum),
1383 jucn->volume_name);
1384 if (!*pp_path_out) {
1385 vfs_ChDir(*conn_out, *oldpath);
1386 SMB_VFS_DISCONNECT(*conn_out);
1387 conn_free(*conn_out);
1388 return False;
1390 return True;
1393 bool create_msdfs_link(const struct junction_map *jucn)
1395 char *path = NULL;
1396 char *cwd;
1397 char *msdfs_link = NULL;
1398 connection_struct *conn;
1399 int i=0;
1400 bool insert_comma = False;
1401 bool ret = False;
1403 if(!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1404 return False;
1407 /* Form the msdfs_link contents */
1408 msdfs_link = talloc_strdup(conn, "msdfs:");
1409 if (!msdfs_link) {
1410 goto out;
1412 for(i=0; i<jucn->referral_count; i++) {
1413 char *refpath = jucn->referral_list[i].alternate_path;
1415 /* Alternate paths always use Windows separators. */
1416 trim_char(refpath, '\\', '\\');
1417 if(*refpath == '\0') {
1418 if (i == 0) {
1419 insert_comma = False;
1421 continue;
1423 if (i > 0 && insert_comma) {
1424 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1425 ",%s",
1426 refpath);
1427 } else {
1428 msdfs_link = talloc_asprintf_append_buffer(msdfs_link,
1429 "%s",
1430 refpath);
1433 if (!msdfs_link) {
1434 goto out;
1436 if (!insert_comma) {
1437 insert_comma = True;
1441 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1442 path, msdfs_link));
1444 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1445 if (errno == EEXIST) {
1446 struct smb_filename *smb_fname = NULL;
1447 NTSTATUS status;
1449 status = create_synthetic_smb_fname(talloc_tos(), path,
1450 NULL, NULL,
1451 &smb_fname);
1452 if (!NT_STATUS_IS_OK(status)) {
1453 errno = map_errno_from_nt_status(status);
1454 goto out;
1457 if(SMB_VFS_UNLINK(conn, smb_fname)!=0) {
1458 TALLOC_FREE(smb_fname);
1459 goto out;
1461 TALLOC_FREE(smb_fname);
1463 if (SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1464 DEBUG(1,("create_msdfs_link: symlink failed "
1465 "%s -> %s\nError: %s\n",
1466 path, msdfs_link, strerror(errno)));
1467 goto out;
1471 ret = True;
1473 out:
1474 vfs_ChDir(conn, cwd);
1475 SMB_VFS_DISCONNECT(conn);
1476 conn_free(conn);
1477 return ret;
1480 bool remove_msdfs_link(const struct junction_map *jucn)
1482 char *path = NULL;
1483 char *cwd;
1484 connection_struct *conn;
1485 bool ret = False;
1486 struct smb_filename *smb_fname = NULL;
1487 NTSTATUS status;
1489 if (!junction_to_local_path(jucn, &path, &conn, &cwd)) {
1490 return false;
1493 status = create_synthetic_smb_fname(talloc_tos(), path,
1494 NULL, NULL,
1495 &smb_fname);
1496 if (!NT_STATUS_IS_OK(status)) {
1497 errno = map_errno_from_nt_status(status);
1498 return false;
1501 if( SMB_VFS_UNLINK(conn, smb_fname) == 0 ) {
1502 ret = True;
1505 TALLOC_FREE(smb_fname);
1506 vfs_ChDir(conn, cwd);
1507 SMB_VFS_DISCONNECT(conn);
1508 conn_free(conn);
1509 return ret;
1512 /*********************************************************************
1513 Return the number of DFS links at the root of this share.
1514 *********************************************************************/
1516 static int count_dfs_links(TALLOC_CTX *ctx, int snum)
1518 size_t cnt = 0;
1519 SMB_STRUCT_DIR *dirp = NULL;
1520 const char *dname = NULL;
1521 char *talloced = NULL;
1522 const char *connect_path = lp_pathname(snum);
1523 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1524 connection_struct *conn;
1525 NTSTATUS status;
1526 char *cwd;
1528 if(*connect_path == '\0') {
1529 return 0;
1533 * Fake up a connection struct for the VFS layer.
1536 status = create_conn_struct(talloc_tos(), &conn, snum, connect_path,
1537 NULL, &cwd);
1538 if (!NT_STATUS_IS_OK(status)) {
1539 DEBUG(3, ("create_conn_struct failed: %s\n",
1540 nt_errstr(status)));
1541 return 0;
1544 /* Count a link for the msdfs root - convention */
1545 cnt = 1;
1547 /* No more links if this is an msdfs proxy. */
1548 if (*msdfs_proxy != '\0') {
1549 goto out;
1552 /* Now enumerate all dfs links */
1553 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1554 if(!dirp) {
1555 goto out;
1558 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1559 != NULL) {
1560 if (is_msdfs_link(conn,
1561 dname,
1562 NULL)) {
1563 cnt++;
1565 TALLOC_FREE(talloced);
1568 SMB_VFS_CLOSEDIR(conn,dirp);
1570 out:
1571 vfs_ChDir(conn, cwd);
1572 SMB_VFS_DISCONNECT(conn);
1573 conn_free(conn);
1574 return cnt;
1577 /*********************************************************************
1578 *********************************************************************/
1580 static int form_junctions(TALLOC_CTX *ctx,
1581 int snum,
1582 struct junction_map *jucn,
1583 size_t jn_remain)
1585 size_t cnt = 0;
1586 SMB_STRUCT_DIR *dirp = NULL;
1587 const char *dname = NULL;
1588 char *talloced = NULL;
1589 const char *connect_path = lp_pathname(snum);
1590 char *service_name = lp_servicename(snum);
1591 const char *msdfs_proxy = lp_msdfs_proxy(snum);
1592 connection_struct *conn;
1593 struct referral *ref = NULL;
1594 char *cwd;
1595 NTSTATUS status;
1597 if (jn_remain == 0) {
1598 return 0;
1601 if(*connect_path == '\0') {
1602 return 0;
1606 * Fake up a connection struct for the VFS layer.
1609 status = create_conn_struct(ctx, &conn, snum, connect_path, NULL,
1610 &cwd);
1611 if (!NT_STATUS_IS_OK(status)) {
1612 DEBUG(3, ("create_conn_struct failed: %s\n",
1613 nt_errstr(status)));
1614 return 0;
1617 /* form a junction for the msdfs root - convention
1618 DO NOT REMOVE THIS: NT clients will not work with us
1619 if this is not present
1621 jucn[cnt].service_name = talloc_strdup(ctx,service_name);
1622 jucn[cnt].volume_name = talloc_strdup(ctx, "");
1623 if (!jucn[cnt].service_name || !jucn[cnt].volume_name) {
1624 goto out;
1626 jucn[cnt].comment = "";
1627 jucn[cnt].referral_count = 1;
1629 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1630 if (jucn[cnt].referral_list == NULL) {
1631 goto out;
1634 ref->proximity = 0;
1635 ref->ttl = REFERRAL_TTL;
1636 if (*msdfs_proxy != '\0') {
1637 ref->alternate_path = talloc_strdup(ctx,
1638 msdfs_proxy);
1639 } else {
1640 ref->alternate_path = talloc_asprintf(ctx,
1641 "\\\\%s\\%s",
1642 get_local_machine_name(),
1643 service_name);
1646 if (!ref->alternate_path) {
1647 goto out;
1649 cnt++;
1651 /* Don't enumerate if we're an msdfs proxy. */
1652 if (*msdfs_proxy != '\0') {
1653 goto out;
1656 /* Now enumerate all dfs links */
1657 dirp = SMB_VFS_OPENDIR(conn, ".", NULL, 0);
1658 if(!dirp) {
1659 goto out;
1662 while ((dname = vfs_readdirname(conn, dirp, NULL, &talloced))
1663 != NULL) {
1664 char *link_target = NULL;
1665 if (cnt >= jn_remain) {
1666 DEBUG(2, ("form_junctions: ran out of MSDFS "
1667 "junction slots"));
1668 TALLOC_FREE(talloced);
1669 goto out;
1671 if (is_msdfs_link_internal(ctx,
1672 conn,
1673 dname, &link_target,
1674 NULL)) {
1675 if (parse_msdfs_symlink(ctx,
1676 link_target,
1677 &jucn[cnt].referral_list,
1678 &jucn[cnt].referral_count)) {
1680 jucn[cnt].service_name = talloc_strdup(ctx,
1681 service_name);
1682 jucn[cnt].volume_name = talloc_strdup(ctx,
1683 dname);
1684 if (!jucn[cnt].service_name ||
1685 !jucn[cnt].volume_name) {
1686 TALLOC_FREE(talloced);
1687 goto out;
1689 jucn[cnt].comment = "";
1690 cnt++;
1692 TALLOC_FREE(link_target);
1694 TALLOC_FREE(talloced);
1697 out:
1699 if (dirp) {
1700 SMB_VFS_CLOSEDIR(conn,dirp);
1703 vfs_ChDir(conn, cwd);
1704 conn_free(conn);
1705 return cnt;
1708 struct junction_map *enum_msdfs_links(TALLOC_CTX *ctx, size_t *p_num_jn)
1710 struct junction_map *jn = NULL;
1711 int i=0;
1712 size_t jn_count = 0;
1713 int sharecount = 0;
1715 *p_num_jn = 0;
1716 if(!lp_host_msdfs()) {
1717 return NULL;
1720 /* Ensure all the usershares are loaded. */
1721 become_root();
1722 load_registry_shares();
1723 sharecount = load_usershare_shares();
1724 unbecome_root();
1726 for(i=0;i < sharecount;i++) {
1727 if(lp_msdfs_root(i)) {
1728 jn_count += count_dfs_links(ctx, i);
1731 if (jn_count == 0) {
1732 return NULL;
1734 jn = TALLOC_ARRAY(ctx, struct junction_map, jn_count);
1735 if (!jn) {
1736 return NULL;
1738 for(i=0; i < sharecount; i++) {
1739 if (*p_num_jn >= jn_count) {
1740 break;
1742 if(lp_msdfs_root(i)) {
1743 *p_num_jn += form_junctions(ctx, i,
1744 &jn[*p_num_jn],
1745 jn_count - *p_num_jn);
1748 return jn;
1751 /******************************************************************************
1752 Core function to resolve a dfs pathname possibly containing a wildcard. If
1753 ppath_contains_wcard != NULL, it will be set to true if a wildcard is
1754 detected during dfs resolution.
1755 ******************************************************************************/
1757 NTSTATUS resolve_dfspath_wcard(TALLOC_CTX *ctx,
1758 connection_struct *conn,
1759 bool dfs_pathnames,
1760 const char *name_in,
1761 bool allow_wcards,
1762 char **pp_name_out,
1763 bool *ppath_contains_wcard)
1765 bool path_contains_wcard;
1766 NTSTATUS status = NT_STATUS_OK;
1768 if (dfs_pathnames) {
1769 status = dfs_redirect(ctx,
1770 conn,
1771 name_in,
1772 allow_wcards,
1773 pp_name_out,
1774 &path_contains_wcard);
1776 if (NT_STATUS_IS_OK(status) && ppath_contains_wcard != NULL) {
1777 *ppath_contains_wcard = path_contains_wcard;
1779 } else {
1781 * Cheat and just return a copy of the in ptr.
1782 * Once srvstr_get_path() uses talloc it'll
1783 * be a talloced ptr anyway.
1785 *pp_name_out = CONST_DISCARD(char *,name_in);
1787 return status;