2 Unix SMB/Netbios implementation.
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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #define DBGC_CLASS DBGC_MSDFS
27 extern uint32 global_client_caps;
29 /**********************************************************************
30 Parse a DFS pathname of the form \hostname\service\reqpath
31 into the dfs_path structure.
32 If POSIX pathnames is true, the pathname may also be of the
33 form /hostname/service/reqpath.
34 We cope with either here.
36 Unfortunately, due to broken clients who might set the
37 SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
38 send a local path, we have to cope with that too....
41 **********************************************************************/
43 static NTSTATUS parse_dfs_path(const char *pathname,
46 BOOL *ppath_contains_wcard)
48 pstring pathname_local;
50 NTSTATUS status = NT_STATUS_OK;
55 pstrcpy(pathname_local,pathname);
56 p = temp = pathname_local;
58 pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
60 sepchar = pdp->posix_path ? '/' : '\\';
62 if (*pathname != sepchar) {
63 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
66 * Possibly client sent a local path by mistake.
67 * Try and convert to a local path.
70 pdp->hostname[0] = '\0';
71 pdp->servicename[0] = '\0';
73 /* We've got no info about separators. */
74 pdp->posix_path = lp_posix_pathnames();
76 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
81 trim_char(temp,sepchar,sepchar);
83 DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
87 /* Parse out hostname. */
88 p = strchr_m(temp,sepchar);
90 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
93 * Possibly client sent a local path by mistake.
94 * Try and convert to a local path.
97 pdp->hostname[0] = '\0';
98 pdp->servicename[0] = '\0';
101 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
106 fstrcpy(pdp->hostname,temp);
107 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
109 /* If we got a hostname, is it ours (or an IP address) ? */
110 if (!is_myname_or_ipaddr(pdp->hostname)) {
113 DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
114 pdp->hostname, temp));
116 * Possibly client sent a local path by mistake.
117 * Try and convert to a local path.
120 pdp->hostname[0] = '\0';
121 pdp->servicename[0] = '\0';
124 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
129 /* Parse out servicename. */
131 p = strchr_m(temp,sepchar);
133 fstrcpy(pdp->servicename,temp);
134 pdp->reqpath[0] = '\0';
138 fstrcpy(pdp->servicename,temp);
139 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
145 *ppath_contains_wcard = False;
147 /* Rest is reqpath. */
148 if (pdp->posix_path) {
149 status = check_path_syntax_posix(pdp->reqpath, p);
152 status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
154 status = check_path_syntax(pdp->reqpath, p);
158 if (!NT_STATUS_IS_OK(status)) {
159 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
160 p, nt_errstr(status) ));
164 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
168 /********************************************************
169 Fake up a connection struct for the VFS layer.
170 Note this CHANGES CWD !!!! JRA.
171 *********************************************************/
173 static BOOL create_conn_struct(connection_struct *conn, int snum, const char *path)
179 pstrcpy(connpath, path);
180 pstring_sub(connpath , "%S", lp_servicename(snum));
182 /* needed for smbd_vfs_init() */
184 if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
185 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
189 if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
190 DEBUG(0, ("TALLOC failed\n"));
194 conn->params->service = snum;
196 set_conn_connectpath(conn, connpath);
198 if (!smbd_vfs_init(conn)) {
199 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
200 conn_free_internal(conn);
205 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
206 * share as the anonymous user. If we try to chdir as that user we will
207 * fail.... WTF ? JRA.
210 if (vfs_ChDir(conn,conn->connectpath) != 0) {
211 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
212 conn->connectpath, strerror(errno) ));
213 conn_free_internal(conn);
220 /**********************************************************************
221 Parse the contents of a symlink to verify if it is an msdfs referral
222 A valid referral is of the form:
224 msdfs:server1\share1,server2\share2
225 msdfs:server1\share1\pathname,server2\share2\pathname
226 msdfs:server1/share1,server2/share2
227 msdfs:server1/share1/pathname,server2/share2/pathname.
229 Note that the alternate paths returned here must be of the canonicalized
233 \server\share\path\to\file,
235 even in posix path mode. This is because we have no knowledge if the
236 server we're referring to understands posix paths.
237 **********************************************************************/
239 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
241 struct referral **preflist,
246 char *alt_path[MAX_REFERRAL_COUNT];
248 struct referral *reflist;
250 pstrcpy(temp,target);
251 prot = strtok(temp,":");
253 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
257 /* parse out the alternate paths */
258 while((count<MAX_REFERRAL_COUNT) &&
259 ((alt_path[count] = strtok(NULL,",")) != NULL)) {
263 DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
265 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
266 if(reflist == NULL) {
267 DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
271 for(i=0;i<count;i++) {
274 /* Canonicalize link target. Replace all /'s in the path by a \ */
275 string_replace(alt_path[i], '/', '\\');
277 /* Remove leading '\\'s */
279 while (*p && (*p == '\\')) {
283 pstrcpy(reflist[i].alternate_path, "\\");
284 pstrcat(reflist[i].alternate_path, p);
286 reflist[i].proximity = 0;
287 reflist[i].ttl = REFERRAL_TTL;
288 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
295 /**********************************************************************
296 Returns true if the unix path is a valid msdfs symlink and also
297 returns the target string from inside the link.
298 **********************************************************************/
300 BOOL is_msdfs_link(connection_struct *conn,
303 SMB_STRUCT_STAT *sbufp)
306 int referral_len = 0;
312 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
313 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
317 if (!S_ISLNK(sbufp->st_mode)) {
318 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
322 /* open the link and read it */
323 referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
324 if (referral_len == -1) {
325 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
326 path, strerror(errno)));
329 link_target[referral_len] = '\0';
331 DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
333 if (!strnequal(link_target, "msdfs:", 6)) {
339 /*****************************************************************
340 Used by other functions to decide if a dfs path is remote,
341 and to get the list of referred locations for that remote path.
343 search_flag: For findfirsts, dfs links themselves are not
344 redirected, but paths beyond the links are. For normal smb calls,
345 even dfs links need to be redirected.
347 consumedcntp: how much of the dfs path is being redirected. the client
348 should try the remaining path on the redirected server.
350 If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
351 link redirect are in targetpath.
352 *****************************************************************/
354 static NTSTATUS dfs_path_lookup(connection_struct *conn,
355 const char *dfspath, /* Incoming complete dfs path */
356 const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
357 BOOL search_flag, /* Called from a findfirst ? */
363 SMB_STRUCT_STAT sbuf;
366 pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
368 DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
369 conn->connectpath, pdp->reqpath));
372 * Note the unix path conversion here we're doing we can
373 * throw away. We're looking for a symlink for a dfs
374 * resolution, if we don't find it we'll do another
375 * unix_convert later in the codepath.
376 * If we needed to remember what we'd resolved in
377 * dp->reqpath (as the original code did) we'd
378 * pstrcpy(localhost, dp->reqpath) on any code
379 * path below that returns True - but I don't
380 * think this is needed. JRA.
383 pstrcpy(localpath, pdp->reqpath);
384 status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
385 if (!NT_STATUS_IS_OK(status)) {
389 /* Optimization - check if we can redirect the whole path. */
391 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
393 DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
394 "for dfs link %s.\n", dfspath));
398 DEBUG(6,("dfs_path_lookup: %s resolves to a "
399 "valid dfs link %s.\n", dfspath, targetpath));
402 *consumedcntp = strlen(dfspath);
404 return NT_STATUS_PATH_NOT_COVERED;
407 /* Prepare to test only for '/' components in the given path,
408 * so if a Windows path replace all '\\' characters with '/'.
409 * For a POSIX DFS path we know all separators are already '/'. */
411 pstrcpy(canon_dfspath, dfspath);
412 if (!pdp->posix_path) {
413 string_replace(canon_dfspath, '\\', '/');
417 * Redirect if any component in the path is a link.
418 * We do this by walking backwards through the
419 * local path, chopping off the last component
420 * in both the local path and the canonicalized
421 * DFS path. If we hit a DFS link then we're done.
424 p = strrchr_m(localpath, '/');
426 q = strrchr_m(canon_dfspath, '/');
435 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
436 DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
437 "parent %s is dfs link\n", dfspath, localpath));
440 *consumedcntp = strlen(canon_dfspath);
441 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
442 "(%d)\n", canon_dfspath, *consumedcntp));
445 return NT_STATUS_PATH_NOT_COVERED;
448 /* Step back on the filesystem. */
449 p = strrchr_m(localpath, '/');
452 /* And in the canonicalized dfs path. */
453 q = strrchr_m(canon_dfspath, '/');
460 /*****************************************************************
461 Decides if a dfs pathname should be redirected or not.
462 If not, the pathname is converted to a tcon-relative local unix path
464 search_wcard_flag: this flag performs 2 functions bother related
465 to searches. See resolve_dfs_path() and parse_dfs_path_XX()
468 This function can return NT_STATUS_OK, meaning use the returned path as-is
469 (mapped into a local path).
470 or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
471 any other NT_STATUS error which is a genuine error to be
472 returned to the client.
473 *****************************************************************/
475 static NTSTATUS dfs_redirect( connection_struct *conn,
477 BOOL search_wcard_flag,
478 BOOL *ppath_contains_wcard)
484 status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
485 if (!NT_STATUS_IS_OK(status)) {
489 if (dp.reqpath[0] == '\0') {
490 pstrcpy(dfs_path, dp.reqpath);
491 DEBUG(5,("dfs_redirect: self-referral.\n"));
495 /* If dfs pathname for a non-dfs share, convert to tcon-relative
496 path and return OK */
498 if (!lp_msdfs_root(SNUM(conn))) {
499 pstrcpy(dfs_path, dp.reqpath);
503 /* If it looked like a local path (zero hostname/servicename)
504 * just treat as a tcon-relative path. */
506 if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') {
507 pstrcpy(dfs_path, dp.reqpath);
511 if (!( strequal(dp.servicename, lp_servicename(SNUM(conn)))
512 || (strequal(dp.servicename, HOMES_NAME)
513 && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
515 /* The given sharename doesn't match this connection. */
517 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
520 status = dfs_path_lookup(conn, dfs_path, &dp,
521 search_wcard_flag, NULL, targetpath);
522 if (!NT_STATUS_IS_OK(status)) {
523 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
524 DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
526 DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
527 dfs_path, nt_errstr(status) ));
532 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
534 /* Form non-dfs tcon-relative path */
535 pstrcpy(dfs_path, dp.reqpath);
537 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
541 /**********************************************************************
542 Return a self referral.
543 **********************************************************************/
545 static BOOL self_ref(TALLOC_CTX *ctx,
546 const char *dfs_path,
547 struct junction_map *jucn,
549 BOOL *self_referralp)
551 struct referral *ref;
553 *self_referralp = True;
555 jucn->referral_count = 1;
556 if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
557 DEBUG(0,("self_ref: talloc failed for referral\n"));
561 pstrcpy(ref->alternate_path,dfs_path);
563 ref->ttl = REFERRAL_TTL;
564 jucn->referral_list = ref;
565 *consumedcntp = strlen(dfs_path);
569 /**********************************************************************
570 Gets valid referrals for a dfs path and fills up the
571 junction_map structure.
572 **********************************************************************/
574 BOOL get_referred_path(TALLOC_CTX *ctx,
575 const char *dfs_path,
576 struct junction_map *jucn,
578 BOOL *self_referralp)
580 struct connection_struct conns;
581 struct connection_struct *conn = &conns;
592 *self_referralp = False;
594 status = parse_dfs_path(dfs_path, False, &dp, &dummy);
595 if (!NT_STATUS_IS_OK(status)) {
599 /* Verify hostname in path */
600 if (!is_myname_or_ipaddr(dp.hostname)) {
601 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
602 dp.hostname, dfs_path));
606 fstrcpy(jucn->service_name, dp.servicename);
607 pstrcpy(jucn->volume_name, dp.reqpath);
609 /* Verify the share is a dfs root */
610 snum = lp_servicenumber(jucn->service_name);
612 if ((snum = find_service(jucn->service_name)) < 0) {
617 if (!lp_msdfs_root(snum)) {
618 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
619 dp.servicename, dfs_path));
624 * Self referrals are tested with a anonymous IPC connection and
625 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
626 * to an empty string). create_conn_struct cd's into the directory and will
627 * fail if it cannot (as the anonymous user). Cope with this.
630 if (dp.reqpath[0] == '\0') {
631 struct referral *ref;
633 if (*lp_msdfs_proxy(snum) == '\0') {
642 * It's an msdfs proxy share. Redirect to
643 * the configured target share.
646 jucn->referral_count = 1;
647 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
648 DEBUG(0, ("malloc failed for referral\n"));
652 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
653 if (dp.reqpath[0] != '\0') {
654 pstrcat(ref->alternate_path, dp.reqpath);
657 ref->ttl = REFERRAL_TTL;
658 jucn->referral_list = ref;
659 *consumedcntp = strlen(dfs_path);
664 pstrcpy(conn_path, lp_pathname(snum));
665 if (!create_conn_struct(conn, snum, conn_path)) {
669 /* If this is a DFS path dfs_lookup should return
670 * NT_STATUS_PATH_NOT_COVERED. */
672 status = dfs_path_lookup(conn, dfs_path, &dp,
673 False, consumedcntp, targetpath);
675 if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
676 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
681 /* We know this is a valid dfs link. Parse the targetpath. */
682 if (!parse_msdfs_symlink(ctx, targetpath,
683 &jucn->referral_list,
684 &jucn->referral_count)) {
685 DEBUG(3,("get_referred_path: failed to parse symlink "
686 "target %s\n", targetpath ));
694 conn_free_internal(conn);
698 static int setup_ver2_dfs_referral(const char *pathname,
700 struct junction_map *junction,
704 char* pdata = *ppdata;
706 unsigned char uni_requestedpath[1024];
707 int uni_reqpathoffset1,uni_reqpathoffset2;
709 int requestedpathlen=0;
714 DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
716 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
720 dump_data(0, uni_requestedpath,requestedpathlen);
723 DEBUG(10,("ref count = %u\n",junction->referral_count));
725 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
726 VERSION2_REFERRAL_SIZE * junction->referral_count;
728 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
730 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
732 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
733 2 * requestedpathlen;
734 DEBUG(10,("reply_size: %u\n",reply_size));
736 /* add up the unicode lengths of all the referral paths */
737 for(i=0;i<junction->referral_count;i++) {
738 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
739 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
742 DEBUG(10,("reply_size = %u\n",reply_size));
743 /* add the unexplained 0x16 bytes */
746 pdata = (char *)SMB_REALLOC(pdata,reply_size);
748 DEBUG(0,("Realloc failed!\n"));
753 /* copy in the dfs requested paths.. required for offset calculations */
754 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
755 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
757 /* create the header */
758 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
759 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
761 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
763 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
767 /* add the referral elements */
768 for(i=0;i<junction->referral_count;i++) {
769 struct referral* ref = &junction->referral_list[i];
772 SSVAL(pdata,offset,2); /* version 2 */
773 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
775 SSVAL(pdata,offset+4,1);
777 SSVAL(pdata,offset+4,0);
779 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
780 SIVAL(pdata,offset+8,ref->proximity);
781 SIVAL(pdata,offset+12,ref->ttl);
783 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
784 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
785 /* copy referred path into current offset */
786 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
787 sizeof(pstring), STR_UNICODE);
789 SSVAL(pdata,offset+20,uni_curroffset-offset);
791 uni_curroffset += unilen;
792 offset += VERSION2_REFERRAL_SIZE;
794 /* add in the unexplained 22 (0x16) bytes at the end */
795 memset(pdata+uni_curroffset,'\0',0x16);
799 static int setup_ver3_dfs_referral(const char *pathname,
801 struct junction_map *junction,
805 char* pdata = *ppdata;
807 unsigned char uni_reqpath[1024];
808 int uni_reqpathoffset1, uni_reqpathoffset2;
815 DEBUG(10,("setting up version3 referral\n"));
817 reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
820 dump_data(0, uni_reqpath,reqpathlen);
823 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
824 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
825 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
827 for(i=0;i<junction->referral_count;i++) {
828 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
829 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
832 pdata = (char *)SMB_REALLOC(pdata,reply_size);
834 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
839 /* create the header */
840 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
841 SSVAL(pdata,2,junction->referral_count); /* number of referral */
843 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
845 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
848 /* copy in the reqpaths */
849 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
850 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
853 for(i=0;i<junction->referral_count;i++) {
854 struct referral* ref = &(junction->referral_list[i]);
857 SSVAL(pdata,offset,3); /* version 3 */
858 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
860 SSVAL(pdata,offset+4,1);
862 SSVAL(pdata,offset+4,0);
865 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
866 SIVAL(pdata,offset+8,ref->ttl);
868 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
869 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
870 /* copy referred path into current offset */
871 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
872 sizeof(pstring), STR_UNICODE | STR_TERMINATE);
873 SSVAL(pdata,offset+16,uni_curroffset-offset);
874 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
875 memset(pdata+offset+18,'\0',16);
877 uni_curroffset += unilen;
878 offset += VERSION3_REFERRAL_SIZE;
883 /******************************************************************
884 Set up the DFS referral for the dfs pathname. This call returns
885 the amount of the path covered by this server, and where the
886 client should be redirected to. This is the meat of the
887 TRANS2_GET_DFS_REFERRAL call.
888 ******************************************************************/
890 int setup_dfs_referral(connection_struct *orig_conn,
891 const char *dfs_path,
892 int max_referral_level,
895 struct junction_map junction;
897 BOOL self_referral = False;
899 char *pathnamep = NULL;
900 pstring local_dfs_path;
903 if (!(ctx=talloc_init("setup_dfs_referral"))) {
907 ZERO_STRUCT(junction);
909 /* get the junction entry */
916 * Trim pathname sent by client so it begins with only one backslash.
917 * Two backslashes confuse some dfs clients
920 pstrcpy(local_dfs_path, dfs_path);
921 pathnamep = local_dfs_path;
922 while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
926 /* The following call can change cwd. */
927 if (!get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral)) {
928 vfs_ChDir(orig_conn,orig_conn->connectpath);
932 vfs_ChDir(orig_conn,orig_conn->connectpath);
934 if (!self_referral) {
935 pathnamep[consumedcnt] = '\0';
937 if( DEBUGLVL( 3 ) ) {
939 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
940 for(i=0;i<junction.referral_count;i++)
941 dbgtext(" %s",junction.referral_list[i].alternate_path);
946 /* create the referral depeding on version */
947 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
949 if (max_referral_level < 2) {
950 max_referral_level = 2;
952 if (max_referral_level > 3) {
953 max_referral_level = 3;
956 switch(max_referral_level) {
958 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
959 consumedcnt, self_referral);
962 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
963 consumedcnt, self_referral);
966 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
972 DEBUGADD(0,("DFS Referral pdata:\n"));
973 dump_data(0,(uint8 *)*ppdata,reply_size);
980 /**********************************************************************
981 The following functions are called by the NETDFS RPC pipe functions
982 **********************************************************************/
984 /*********************************************************************
985 Creates a junction structure from a DFS pathname
986 **********************************************************************/
988 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
994 NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
996 if (!NT_STATUS_IS_OK(status)) {
1000 /* check if path is dfs : validate first token */
1001 if (!is_myname_or_ipaddr(dp.hostname)) {
1002 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
1003 dp.hostname, dfs_path));
1007 /* Check for a non-DFS share */
1008 snum = lp_servicenumber(dp.servicename);
1010 if(snum < 0 || !lp_msdfs_root(snum)) {
1011 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
1016 fstrcpy(jucn->service_name,dp.servicename);
1017 pstrcpy(jucn->volume_name,dp.reqpath);
1018 pstrcpy(jucn->comment, lp_comment(snum));
1022 /**********************************************************************
1023 Forms a valid Unix pathname from the junction
1024 **********************************************************************/
1026 static BOOL junction_to_local_path(struct junction_map *jucn,
1029 connection_struct *conn_out)
1034 snum = lp_servicenumber(jucn->service_name);
1039 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
1040 safe_strcat(path, "/", max_pathlen-1);
1041 safe_strcat(path, jucn->volume_name, max_pathlen-1);
1043 pstrcpy(conn_path, lp_pathname(snum));
1044 if (!create_conn_struct(conn_out, snum, conn_path)) {
1051 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
1055 connection_struct conns;
1056 connection_struct *conn = &conns;
1058 BOOL insert_comma = False;
1063 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
1067 /* Form the msdfs_link contents */
1068 pstrcpy(msdfs_link, "msdfs:");
1069 for(i=0; i<jucn->referral_count; i++) {
1070 char* refpath = jucn->referral_list[i].alternate_path;
1072 /* Alternate paths always use Windows separators. */
1073 trim_char(refpath, '\\', '\\');
1074 if(*refpath == '\0') {
1076 insert_comma = False;
1080 if (i > 0 && insert_comma) {
1081 pstrcat(msdfs_link, ",");
1084 pstrcat(msdfs_link, refpath);
1085 if (!insert_comma) {
1086 insert_comma = True;
1090 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
1094 if(SMB_VFS_UNLINK(conn,path)!=0) {
1099 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1100 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
1101 path, msdfs_link, strerror(errno)));
1110 conn_free_internal(conn);
1114 BOOL remove_msdfs_link(struct junction_map *jucn)
1117 connection_struct conns;
1118 connection_struct *conn = &conns;
1123 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1124 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1127 talloc_destroy( conn->mem_ctx );
1130 conn_free_internal(conn);
1134 static int form_junctions(TALLOC_CTX *ctx,
1136 struct junction_map *jucn,
1140 SMB_STRUCT_DIR *dirp;
1142 pstring connect_path;
1143 char *service_name = lp_servicename(snum);
1144 connection_struct conn;
1145 struct referral *ref = NULL;
1149 if (jn_remain <= 0) {
1153 pstrcpy(connect_path,lp_pathname(snum));
1155 if(*connect_path == '\0') {
1160 * Fake up a connection struct for the VFS layer.
1163 if (!create_conn_struct(&conn, snum, connect_path)) {
1167 /* form a junction for the msdfs root - convention
1168 DO NOT REMOVE THIS: NT clients will not work with us
1169 if this is not present
1171 fstrcpy(jucn[cnt].service_name, service_name);
1172 jucn[cnt].volume_name[0] = '\0';
1173 jucn[cnt].referral_count = 1;
1175 ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
1176 if (jucn[cnt].referral_list == NULL) {
1177 DEBUG(0, ("talloc failed!\n"));
1182 ref->ttl = REFERRAL_TTL;
1183 if (*lp_msdfs_proxy(snum) != '\0') {
1184 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1189 pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
1190 get_local_machine_name(),
1194 /* Now enumerate all dfs links */
1195 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1200 while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1201 pstring link_target;
1202 if (cnt >= jn_remain) {
1203 SMB_VFS_CLOSEDIR(&conn,dirp);
1204 DEBUG(2, ("ran out of MSDFS junction slots"));
1207 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
1208 if (parse_msdfs_symlink(ctx,
1210 &jucn[cnt].referral_list,
1211 &jucn[cnt].referral_count)) {
1213 fstrcpy(jucn[cnt].service_name, service_name);
1214 pstrcpy(jucn[cnt].volume_name, dname);
1220 SMB_VFS_CLOSEDIR(&conn,dirp);
1224 conn_free_internal(&conn);
1228 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1234 if(!lp_host_msdfs()) {
1238 /* Ensure all the usershares are loaded. */
1240 load_registry_shares();
1241 sharecount = load_usershare_shares();
1244 for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1245 if(lp_msdfs_root(i)) {
1246 jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1252 /******************************************************************************
1253 Core function to resolve a dfs pathname.
1254 ******************************************************************************/
1256 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
1258 NTSTATUS status = NT_STATUS_OK;
1260 if (dfs_pathnames) {
1261 status = dfs_redirect(conn, name, False, &dummy);
1266 /******************************************************************************
1267 Core function to resolve a dfs pathname possibly containing a wildcard.
1268 This function is identical to the above except for the BOOL param to
1269 dfs_redirect but I need this to be separate so it's really clear when
1270 we're allowing wildcards and when we're not. JRA.
1271 ******************************************************************************/
1273 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
1275 NTSTATUS status = NT_STATUS_OK;
1276 if (dfs_pathnames) {
1277 status = dfs_redirect(conn, name, True, ppath_contains_wcard);