r6334: revert 3.0.15pre1 changes. roll back to 3.0.14.
[Samba.git] / source / smbd / msdfs.c
blob184dde1635437956a02727fa9e2549b4c277fcc8
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDfs services for Samba
5 Copyright (C) Shirish Kalele 2000
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
25 extern uint32 global_client_caps;
27 /**********************************************************************
28 Parse the pathname of the form \hostname\service\reqpath
29 into the dfs_path structure
30 **********************************************************************/
32 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
34 pstring pathname_local;
35 char* p,*temp;
37 pstrcpy(pathname_local,pathname);
38 p = temp = pathname_local;
40 ZERO_STRUCTP(pdp);
42 trim_char(temp,'\\','\\');
43 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
45 /* now tokenize */
46 /* parse out hostname */
47 p = strchr_m(temp,'\\');
48 if(p == NULL)
49 return False;
50 *p = '\0';
51 pstrcpy(pdp->hostname,temp);
52 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
54 /* parse out servicename */
55 temp = p+1;
56 p = strchr_m(temp,'\\');
57 if(p == NULL) {
58 pstrcpy(pdp->servicename,temp);
59 pdp->reqpath[0] = '\0';
60 return True;
62 *p = '\0';
63 pstrcpy(pdp->servicename,temp);
64 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
66 /* rest is reqpath */
67 check_path_syntax(pdp->reqpath, p+1,True);
69 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
70 return True;
73 /**********************************************************************
74 Parse the pathname of the form /hostname/service/reqpath
75 into the dfs_path structure
76 **********************************************************************/
78 static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
80 pstring pathname_local;
81 char* p,*temp;
83 pstrcpy(pathname_local,pathname);
84 p = temp = pathname_local;
86 ZERO_STRUCTP(pdp);
88 trim_char(temp,'/','/');
89 DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
91 /* now tokenize */
92 /* parse out hostname */
93 p = strchr_m(temp,'/');
94 if(p == NULL)
95 return False;
96 *p = '\0';
97 pstrcpy(pdp->hostname,temp);
98 DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
100 /* parse out servicename */
101 temp = p+1;
102 p = strchr_m(temp,'/');
103 if(p == NULL) {
104 pstrcpy(pdp->servicename,temp);
105 pdp->reqpath[0] = '\0';
106 return True;
108 *p = '\0';
109 pstrcpy(pdp->servicename,temp);
110 DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
112 /* rest is reqpath */
113 check_path_syntax(pdp->reqpath, p+1,True);
115 DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
116 return True;
119 /********************************************************
120 Fake up a connection struct for the VFS layer.
121 Note this CHANGES CWD !!!! JRA.
122 *********************************************************/
124 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
126 ZERO_STRUCTP(conn);
127 conn->service = snum;
128 conn->connectpath = path;
129 pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
131 /* needed for smbd_vfs_init() */
133 if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
134 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
135 return False;
138 if (!smbd_vfs_init(conn)) {
139 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
140 talloc_destroy( conn->mem_ctx );
141 return False;
145 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
146 * share as the anonymous user. If we try to chdir as that user we will
147 * fail.... WTF ? JRA.
150 if (vfs_ChDir(conn,conn->connectpath) != 0) {
151 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
152 conn->connectpath, strerror(errno) ));
153 talloc_destroy( conn->mem_ctx );
154 return False;
156 return True;
160 /**********************************************************************
161 Parse the contents of a symlink to verify if it is an msdfs referral
162 A valid referral is of the form: msdfs:server1\share1,server2\share2
163 **********************************************************************/
165 static BOOL parse_symlink(char* buf,struct referral** preflist,
166 int* refcount)
168 pstring temp;
169 char* prot;
170 char* alt_path[MAX_REFERRAL_COUNT];
171 int count=0, i;
172 struct referral* reflist;
174 pstrcpy(temp,buf);
176 prot = strtok(temp,":");
178 if (!strequal(prot, "msdfs"))
179 return False;
181 /* No referral list requested. Just yes/no. */
182 if (!preflist)
183 return True;
185 /* parse out the alternate paths */
186 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
187 count++;
189 DEBUG(10,("parse_symlink: count=%d\n", count));
191 reflist = *preflist = SMB_MALLOC_ARRAY(struct referral, count);
192 if(reflist == NULL) {
193 DEBUG(0,("parse_symlink: Malloc failed!\n"));
194 return False;
197 for(i=0;i<count;i++) {
198 char *p;
200 /* replace all /'s in the alternate path by a \ */
201 for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
202 *p = '\\';
205 /* Remove leading '\\'s */
206 p = alt_path[i];
207 while (*p && (*p == '\\')) {
208 p++;
211 pstrcpy(reflist[i].alternate_path, "\\");
212 pstrcat(reflist[i].alternate_path, p);
213 reflist[i].proximity = 0;
214 reflist[i].ttl = REFERRAL_TTL;
215 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
218 if(refcount)
219 *refcount = count;
221 return True;
224 /**********************************************************************
225 Returns true if the unix path is a valid msdfs symlink
226 **********************************************************************/
228 BOOL is_msdfs_link(connection_struct* conn, char * path,
229 struct referral** reflistp, int* refcnt,
230 SMB_STRUCT_STAT *sbufp)
232 SMB_STRUCT_STAT st;
233 pstring referral;
234 int referral_len = 0;
236 if (!path || !conn)
237 return False;
239 if (sbufp == NULL)
240 sbufp = &st;
242 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
243 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
244 return False;
247 if (S_ISLNK(sbufp->st_mode)) {
248 /* open the link and read it */
249 referral_len = SMB_VFS_READLINK(conn, path, referral,
250 sizeof(pstring));
251 if (referral_len == -1) {
252 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
253 return False;
256 referral[referral_len] = '\0';
257 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
258 if (parse_symlink(referral, reflistp, refcnt))
259 return True;
261 return False;
264 /*****************************************************************
265 Used by other functions to decide if a dfs path is remote,
266 and to get the list of referred locations for that remote path.
268 findfirst_flag: For findfirsts, dfs links themselves are not
269 redirected, but paths beyond the links are. For normal smb calls,
270 even dfs links need to be redirected.
272 self_referralp: clients expect a dfs referral for the same share when
273 they request referrals for dfs roots on a server.
275 consumedcntp: how much of the dfs path is being redirected. the client
276 should try the remaining path on the redirected server.
278 *****************************************************************/
280 static BOOL resolve_dfs_path(pstring dfspath, struct dfs_path* dp,
281 connection_struct* conn,
282 BOOL findfirst_flag,
283 struct referral** reflistpp, int* refcntp,
284 BOOL* self_referralp, int* consumedcntp)
286 pstring localpath;
287 int consumed_level = 1;
288 char *p;
289 BOOL bad_path = False;
290 SMB_STRUCT_STAT sbuf;
291 pstring reqpath;
293 if (!dp || !conn) {
294 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
295 return False;
298 if (dp->reqpath[0] == '\0') {
299 if (self_referralp) {
300 DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
301 *self_referralp = True;
303 return False;
306 DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
308 unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
309 /* JRA... should we strlower the last component here.... ? */
310 pstrcpy(localpath, dp->reqpath);
312 /* check if need to redirect */
313 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
314 if (findfirst_flag) {
315 DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
316 "for dfs link %s.\n", dfspath));
317 return False;
318 } else {
319 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
320 dfspath));
321 if (consumedcntp)
322 *consumedcntp = strlen(dfspath);
323 return True;
327 /* redirect if any component in the path is a link */
328 pstrcpy(reqpath, dp->reqpath);
329 p = strrchr_m(reqpath, '/');
330 while (p) {
331 *p = '\0';
332 pstrcpy(localpath, reqpath);
333 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
334 DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
336 /* To find the path consumed, we truncate the original
337 DFS pathname passed to use to remove the last
338 component. The length of the resulting string is
339 the path consumed
341 if (consumedcntp) {
342 char *q;
343 pstring buf;
344 pstrcpy(buf, dfspath);
345 trim_char(buf, '\0', '\\');
346 for (; consumed_level; consumed_level--) {
347 q = strrchr_m(buf, '\\');
348 if (q)
349 *q = 0;
351 *consumedcntp = strlen(buf);
352 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
355 return True;
357 p = strrchr_m(reqpath, '/');
358 consumed_level++;
361 return False;
364 /*****************************************************************
365 Decides if a dfs pathname should be redirected or not.
366 If not, the pathname is converted to a tcon-relative local unix path
367 *****************************************************************/
369 BOOL dfs_redirect(pstring pathname, connection_struct* conn,
370 BOOL findfirst_flag)
372 struct dfs_path dp;
374 if (!conn || !pathname)
375 return False;
377 parse_processed_dfs_path(pathname, &dp);
379 /* if dfs pathname for a non-dfs share, convert to tcon-relative
380 path and return false */
381 if (!lp_msdfs_root(SNUM(conn))) {
382 pstrcpy(pathname, dp.reqpath);
383 return False;
386 if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) ))
387 return False;
389 if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
390 NULL, NULL, NULL, NULL)) {
391 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
392 return True;
393 } else {
394 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
396 /* Form non-dfs tcon-relative path */
397 pstrcpy(pathname, dp.reqpath);
398 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
399 pathname));
400 return False;
403 /* never reached */
406 /**********************************************************************
407 Return a self referral.
408 **********************************************************************/
410 static BOOL self_ref(char *pathname, struct junction_map *jucn,
411 int *consumedcntp, BOOL *self_referralp)
413 struct referral *ref;
415 if (self_referralp != NULL)
416 *self_referralp = True;
418 jucn->referral_count = 1;
419 if((ref = SMB_MALLOC_P(struct referral)) == NULL) {
420 DEBUG(0,("self_ref: malloc failed for referral\n"));
421 return False;
424 pstrcpy(ref->alternate_path,pathname);
425 ref->proximity = 0;
426 ref->ttl = REFERRAL_TTL;
427 jucn->referral_list = ref;
428 if (consumedcntp)
429 *consumedcntp = strlen(pathname);
431 return True;
434 /**********************************************************************
435 Gets valid referrals for a dfs path and fills up the
436 junction_map structure
437 **********************************************************************/
439 BOOL get_referred_path(char *pathname, struct junction_map *jucn,
440 int *consumedcntp, BOOL *self_referralp)
442 struct dfs_path dp;
444 struct connection_struct conns;
445 struct connection_struct* conn = &conns;
446 pstring conn_path;
447 int snum;
448 BOOL ret = False;
449 BOOL self_referral = False;
451 if (!pathname || !jucn)
452 return False;
454 ZERO_STRUCT(conns);
456 if (self_referralp)
457 *self_referralp = False;
458 else
459 self_referralp = &self_referral;
461 parse_dfs_path(pathname, &dp);
463 /* Verify hostname in path */
464 if ( !strequal(get_local_machine_name(), dp.hostname) ) {
465 /* Hostname mismatch, check if one of our IP addresses */
466 if (!ismyip(*interpret_addr2(dp.hostname))) {
467 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
468 dp.hostname, pathname));
469 return False;
473 pstrcpy(jucn->service_name, dp.servicename);
474 pstrcpy(jucn->volume_name, dp.reqpath);
476 /* Verify the share is a dfs root */
477 snum = lp_servicenumber(jucn->service_name);
478 if(snum < 0) {
479 if ((snum = find_service(jucn->service_name)) < 0)
480 return False;
483 if (!lp_msdfs_root(snum)) {
484 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
485 dp.servicename, pathname));
486 goto out;
490 * Self referrals are tested with a anonymous IPC connection and
491 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
492 * to an empty string). create_conn_struct cd's into the directory and will
493 * fail if it cannot (as the anonymous user). Cope with this.
496 if (dp.reqpath[0] == '\0') {
498 struct referral* ref;
500 if (*lp_msdfs_proxy(snum) == '\0')
501 return self_ref(pathname, jucn, consumedcntp,
502 self_referralp);
504 jucn->referral_count = 1;
505 if ((ref = SMB_MALLOC_P(struct referral)) == NULL) {
506 DEBUG(0, ("malloc failed for referral\n"));
507 goto out;
510 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
511 if (dp.reqpath[0] != '\0')
512 pstrcat(ref->alternate_path, dp.reqpath);
513 ref->proximity = 0;
514 ref->ttl = REFERRAL_TTL;
515 jucn->referral_list = ref;
516 if (consumedcntp)
517 *consumedcntp = strlen(pathname);
518 ret = True;
519 goto out;
522 pstrcpy(conn_path, lp_pathname(snum));
523 if (!create_conn_struct(conn, snum, conn_path))
524 return False;
526 /* If not remote & not a self referral, return False */
527 if (!resolve_dfs_path(pathname, &dp, conn, False,
528 &jucn->referral_list, &jucn->referral_count,
529 self_referralp, consumedcntp)) {
530 if (!*self_referralp) {
531 DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
532 goto out;
536 /* if self_referral, fill up the junction map */
537 if (*self_referralp) {
538 if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
539 goto out;
543 ret = True;
545 out:
546 if (conn->mem_ctx)
547 talloc_destroy( conn->mem_ctx );
549 return ret;
552 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
553 struct junction_map* junction,
554 int consumedcnt,
555 BOOL self_referral)
557 char* pdata = *ppdata;
559 unsigned char uni_requestedpath[1024];
560 int uni_reqpathoffset1,uni_reqpathoffset2;
561 int uni_curroffset;
562 int requestedpathlen=0;
563 int offset;
564 int reply_size = 0;
565 int i=0;
567 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
569 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
570 STR_TERMINATE);
572 dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
574 DEBUG(10,("ref count = %u\n",junction->referral_count));
576 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
577 VERSION2_REFERRAL_SIZE * junction->referral_count;
579 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
581 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
583 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
584 2 * requestedpathlen;
585 DEBUG(10,("reply_size: %u\n",reply_size));
587 /* add up the unicode lengths of all the referral paths */
588 for(i=0;i<junction->referral_count;i++) {
589 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
590 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
593 DEBUG(10,("reply_size = %u\n",reply_size));
594 /* add the unexplained 0x16 bytes */
595 reply_size += 0x16;
597 pdata = SMB_REALLOC(pdata,reply_size);
598 if(pdata == NULL) {
599 DEBUG(0,("malloc failed for Realloc!\n"));
600 return -1;
601 } else
602 *ppdata = pdata;
604 /* copy in the dfs requested paths.. required for offset calculations */
605 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
606 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
608 /* create the header */
609 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
610 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
611 if(self_referral)
612 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
613 else
614 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
616 offset = 8;
617 /* add the referral elements */
618 for(i=0;i<junction->referral_count;i++) {
619 struct referral* ref = &junction->referral_list[i];
620 int unilen;
622 SSVAL(pdata,offset,2); /* version 2 */
623 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
624 if(self_referral)
625 SSVAL(pdata,offset+4,1);
626 else
627 SSVAL(pdata,offset+4,0);
628 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
629 SIVAL(pdata,offset+8,ref->proximity);
630 SIVAL(pdata,offset+12,ref->ttl);
632 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
633 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
634 /* copy referred path into current offset */
635 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
636 -1, STR_UNICODE);
638 SSVAL(pdata,offset+20,uni_curroffset-offset);
640 uni_curroffset += unilen;
641 offset += VERSION2_REFERRAL_SIZE;
643 /* add in the unexplained 22 (0x16) bytes at the end */
644 memset(pdata+uni_curroffset,'\0',0x16);
645 return reply_size;
648 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
649 struct junction_map* junction,
650 int consumedcnt,
651 BOOL self_referral)
653 char* pdata = *ppdata;
655 unsigned char uni_reqpath[1024];
656 int uni_reqpathoffset1, uni_reqpathoffset2;
657 int uni_curroffset;
658 int reply_size = 0;
660 int reqpathlen = 0;
661 int offset,i=0;
663 DEBUG(10,("setting up version3 referral\n"));
665 reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
667 dump_data(10, (char *) uni_reqpath,reqpathlen);
669 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
670 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
671 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
673 for(i=0;i<junction->referral_count;i++) {
674 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
675 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
678 pdata = SMB_REALLOC(pdata,reply_size);
679 if(pdata == NULL) {
680 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
681 return -1;
682 } else
683 *ppdata = pdata;
685 /* create the header */
686 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
687 SSVAL(pdata,2,junction->referral_count); /* number of referral */
688 if(self_referral)
689 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
690 else
691 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
693 /* copy in the reqpaths */
694 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
695 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
697 offset = 8;
698 for(i=0;i<junction->referral_count;i++) {
699 struct referral* ref = &(junction->referral_list[i]);
700 int unilen;
702 SSVAL(pdata,offset,3); /* version 3 */
703 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
704 if(self_referral)
705 SSVAL(pdata,offset+4,1);
706 else
707 SSVAL(pdata,offset+4,0);
709 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
710 SIVAL(pdata,offset+8,ref->ttl);
712 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
713 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
714 /* copy referred path into current offset */
715 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
716 -1, STR_UNICODE | STR_TERMINATE);
717 SSVAL(pdata,offset+16,uni_curroffset-offset);
718 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
719 memset(pdata+offset+18,'\0',16);
721 uni_curroffset += unilen;
722 offset += VERSION3_REFERRAL_SIZE;
724 return reply_size;
727 /******************************************************************
728 * Set up the Dfs referral for the dfs pathname
729 ******************************************************************/
731 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
733 struct junction_map junction;
734 int consumedcnt;
735 BOOL self_referral = False;
736 pstring buf;
737 int reply_size = 0;
738 char *pathnamep = pathname;
740 ZERO_STRUCT(junction);
742 /* get the junction entry */
743 if (!pathnamep)
744 return -1;
746 /* Trim pathname sent by client so it begins with only one backslash.
747 Two backslashes confuse some dfs clients
749 while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
750 pathnamep++;
752 pstrcpy(buf, pathnamep);
753 /* The following call can change cwd. */
754 if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
755 vfs_ChDir(orig_conn,orig_conn->connectpath);
756 return -1;
758 vfs_ChDir(orig_conn,orig_conn->connectpath);
760 if (!self_referral) {
761 pathnamep[consumedcnt] = '\0';
763 if( DEBUGLVL( 3 ) ) {
764 int i=0;
765 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
766 for(i=0;i<junction.referral_count;i++)
767 dbgtext(" %s",junction.referral_list[i].alternate_path);
768 dbgtext(".\n");
772 /* create the referral depeding on version */
773 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
774 if(max_referral_level<2 || max_referral_level>3)
775 max_referral_level = 2;
777 switch(max_referral_level) {
778 case 2:
779 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
780 consumedcnt, self_referral);
781 SAFE_FREE(junction.referral_list);
782 break;
783 case 3:
784 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
785 consumedcnt, self_referral);
786 SAFE_FREE(junction.referral_list);
787 break;
788 default:
789 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
790 return -1;
793 DEBUG(10,("DFS Referral pdata:\n"));
794 dump_data(10,*ppdata,reply_size);
795 return reply_size;
798 /**********************************************************************
799 The following functions are called by the NETDFS RPC pipe functions
800 **********************************************************************/
802 /**********************************************************************
803 Creates a junction structure from a Dfs pathname
804 **********************************************************************/
805 BOOL create_junction(char* pathname, struct junction_map* jucn)
807 struct dfs_path dp;
809 parse_dfs_path(pathname,&dp);
811 /* check if path is dfs : validate first token */
812 if ( !strequal(get_local_machine_name(),dp.hostname) ) {
814 /* Hostname mismatch, check if one of our IP addresses */
815 if (!ismyip(*interpret_addr2(dp.hostname))) {
816 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
817 dp.hostname, pathname));
818 return False;
822 /* Check for a non-DFS share */
823 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
824 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
825 dp.servicename));
826 return False;
829 pstrcpy(jucn->service_name,dp.servicename);
830 pstrcpy(jucn->volume_name,dp.reqpath);
831 return True;
834 /**********************************************************************
835 Forms a valid Unix pathname from the junction
836 **********************************************************************/
838 static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
839 int max_pathlen, connection_struct *conn)
841 int snum;
842 pstring conn_path;
844 if(!path || !jucn)
845 return False;
847 snum = lp_servicenumber(jucn->service_name);
848 if(snum < 0)
849 return False;
851 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
852 safe_strcat(path, "/", max_pathlen-1);
853 safe_strcat(path, jucn->volume_name, max_pathlen-1);
855 pstrcpy(conn_path, lp_pathname(snum));
856 if (!create_conn_struct(conn, snum, conn_path))
857 return False;
859 return True;
862 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
864 pstring path;
865 pstring msdfs_link;
866 connection_struct conns;
867 connection_struct *conn = &conns;
868 int i=0;
869 BOOL insert_comma = False;
870 BOOL ret = False;
872 if(!junction_to_local_path(jucn, path, sizeof(path), conn))
873 return False;
875 /* form the msdfs_link contents */
876 pstrcpy(msdfs_link, "msdfs:");
877 for(i=0; i<jucn->referral_count; i++) {
878 char* refpath = jucn->referral_list[i].alternate_path;
880 trim_char(refpath, '\\', '\\');
881 if(*refpath == '\0') {
882 if (i == 0)
883 insert_comma = False;
884 continue;
886 if (i > 0 && insert_comma)
887 pstrcat(msdfs_link, ",");
889 pstrcat(msdfs_link, refpath);
890 if (!insert_comma)
891 insert_comma = True;
895 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
897 if(exists)
898 if(SMB_VFS_UNLINK(conn,path)!=0)
899 goto out;
901 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
902 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
903 path, msdfs_link, strerror(errno)));
904 goto out;
908 ret = True;
910 out:
911 talloc_destroy( conn->mem_ctx );
912 return ret;
915 BOOL remove_msdfs_link(struct junction_map* jucn)
917 pstring path;
918 connection_struct conns;
919 connection_struct *conn = &conns;
920 BOOL ret = False;
922 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
923 if( SMB_VFS_UNLINK(conn, path) == 0 )
924 ret = True;
926 talloc_destroy( conn->mem_ctx );
929 return ret;
932 static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
934 int cnt = *jn_count;
935 DIR *dirp;
936 char* dname;
937 pstring connect_path;
938 char* service_name = lp_servicename(snum);
939 connection_struct conns;
940 connection_struct *conn = &conns;
941 struct referral *ref = NULL;
942 BOOL ret = False;
944 pstrcpy(connect_path,lp_pathname(snum));
946 if(*connect_path == '\0')
947 return False;
950 * Fake up a connection struct for the VFS layer.
953 if (!create_conn_struct(conn, snum, connect_path))
954 return False;
956 /* form a junction for the msdfs root - convention
957 DO NOT REMOVE THIS: NT clients will not work with us
958 if this is not present
960 pstrcpy(jucn[cnt].service_name, service_name);
961 jucn[cnt].volume_name[0] = '\0';
962 jucn[cnt].referral_count = 1;
964 ref = jucn[cnt].referral_list = SMB_MALLOC_P(struct referral);
965 if (jucn[cnt].referral_list == NULL) {
966 DEBUG(0, ("Malloc failed!\n"));
967 goto out;
970 ref->proximity = 0;
971 ref->ttl = REFERRAL_TTL;
972 if (*lp_msdfs_proxy(snum) != '\0') {
973 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
974 *jn_count = ++cnt;
975 ret = True;
976 goto out;
979 slprintf(ref->alternate_path, sizeof(pstring)-1,
980 "\\\\%s\\%s", get_local_machine_name(), service_name);
981 cnt++;
983 /* Now enumerate all dfs links */
984 dirp = SMB_VFS_OPENDIR(conn, ".");
985 if(!dirp)
986 goto out;
988 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
989 if (is_msdfs_link(conn, dname, &(jucn[cnt].referral_list),
990 &(jucn[cnt].referral_count), NULL)) {
991 pstrcpy(jucn[cnt].service_name, service_name);
992 pstrcpy(jucn[cnt].volume_name, dname);
993 cnt++;
997 SMB_VFS_CLOSEDIR(conn,dirp);
998 *jn_count = cnt;
999 out:
1000 talloc_destroy(conn->mem_ctx);
1001 return ret;
1004 int enum_msdfs_links(struct junction_map* jucn)
1006 int i=0;
1007 int jn_count = 0;
1009 if(!lp_host_msdfs())
1010 return 0;
1012 for(i=0;i < lp_numservices();i++) {
1013 if(lp_msdfs_root(i))
1014 form_junctions(i,jucn,&jn_count);
1016 return jn_count;