syncing files for rc3
[Samba.git] / source / msdfs / msdfs.c
blob4c86cd0f94bb29e06e58d94de3603259df9387c7
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 fstring local_machine;
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse the pathname of the form \hostname\service\reqpath
30 into the dfs_path structure
31 **********************************************************************/
33 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
35 pstring pathname_local;
36 char* p,*temp;
38 pstrcpy(pathname_local,pathname);
39 p = temp = pathname_local;
41 ZERO_STRUCTP(pdp);
43 trim_char(temp,'\\','\\');
44 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
46 /* now tokenize */
47 /* parse out hostname */
48 p = strchr(temp,'\\');
49 if(p == NULL)
50 return False;
51 *p = '\0';
52 pstrcpy(pdp->hostname,temp);
53 DEBUG(10,("hostname: %s\n",pdp->hostname));
55 /* parse out servicename */
56 temp = p+1;
57 p = strchr(temp,'\\');
58 if(p == NULL) {
59 pstrcpy(pdp->servicename,temp);
60 pdp->reqpath[0] = '\0';
61 return True;
63 *p = '\0';
64 pstrcpy(pdp->servicename,temp);
65 DEBUG(10,("servicename: %s\n",pdp->servicename));
67 /* rest is reqpath */
68 pstrcpy(pdp->reqpath, p+1);
69 p = pdp->reqpath;
70 while (*p) {
71 if (*p == '\\') *p = '/';
72 p++;
75 DEBUG(10,("rest of the path: %s\n",pdp->reqpath));
76 return True;
79 /********************************************************
80 Fake up a connection struct for the VFS layer.
81 *********************************************************/
83 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
85 ZERO_STRUCTP(conn);
86 conn->service = snum;
87 conn->connectpath = path;
88 pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
90 /* needed for smbd_vfs_init() */
92 if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
93 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
94 return False;
97 if (!smbd_vfs_init(conn)) {
98 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
99 talloc_destroy( conn->mem_ctx );
100 return False;
102 return True;
106 /**********************************************************************
107 Parse the contents of a symlink to verify if it is an msdfs referral
108 A valid referral is of the form: msdfs:server1\share1,server2\share2
109 **********************************************************************/
110 static BOOL parse_symlink(char* buf,struct referral** preflist,
111 int* refcount)
113 pstring temp;
114 char* prot;
115 char* alt_path[MAX_REFERRAL_COUNT];
116 int count=0, i;
117 struct referral* reflist;
119 pstrcpy(temp,buf);
121 prot = strtok(temp,":");
123 if (!strequal(prot, "msdfs"))
124 return False;
126 /* No referral list requested. Just yes/no. */
127 if (!preflist)
128 return True;
130 /* parse out the alternate paths */
131 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
132 count++;
134 DEBUG(10,("parse_symlink: count=%d\n", count));
136 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
137 if(reflist == NULL) {
138 DEBUG(0,("parse_symlink: Malloc failed!\n"));
139 return False;
142 for(i=0;i<count;i++) {
143 /* replace / in the alternate path by a \ */
144 char* p = strchr(alt_path[i],'/');
145 if(p)
146 *p = '\\';
148 pstrcpy(reflist[i].alternate_path, "\\");
149 pstrcat(reflist[i].alternate_path, alt_path[i]);
150 reflist[i].proximity = 0;
151 reflist[i].ttl = REFERRAL_TTL;
152 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
155 if(refcount)
156 *refcount = count;
158 return True;
161 /**********************************************************************
162 Returns true if the unix path is a valid msdfs symlink
163 **********************************************************************/
164 BOOL is_msdfs_link(connection_struct* conn, char* path,
165 struct referral** reflistp, int* refcnt,
166 SMB_STRUCT_STAT *sbufp)
168 SMB_STRUCT_STAT st;
169 pstring referral;
170 int referral_len = 0;
172 if (!path || !conn)
173 return False;
175 strlower_m(path);
177 if (sbufp == NULL)
178 sbufp = &st;
180 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
181 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
182 return False;
185 if (S_ISLNK(sbufp->st_mode)) {
186 /* open the link and read it */
187 referral_len = SMB_VFS_READLINK(conn, path, referral,
188 sizeof(pstring));
189 if (referral_len == -1) {
190 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
191 return False;
194 referral[referral_len] = '\0';
195 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
196 if (parse_symlink(referral, reflistp, refcnt))
197 return True;
199 return False;
202 /*****************************************************************
203 Used by other functions to decide if a dfs path is remote,
204 and to get the list of referred locations for that remote path.
206 findfirst_flag: For findfirsts, dfs links themselves are not
207 redirected, but paths beyond the links are. For normal smb calls,
208 even dfs links need to be redirected.
210 self_referralp: clients expect a dfs referral for the same share when
211 they request referrals for dfs roots on a server.
213 consumedcntp: how much of the dfs path is being redirected. the client
214 should try the remaining path on the redirected server.
215 *****************************************************************/
216 static BOOL resolve_dfs_path(char* dfspath, struct dfs_path* dp,
217 connection_struct* conn,
218 BOOL findfirst_flag,
219 struct referral** reflistpp, int* refcntp,
220 BOOL* self_referralp, int* consumedcntp)
222 fstring localpath;
223 int consumed_level = 1;
224 char *p;
225 fstring reqpath;
227 if (!dp || !conn) {
228 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
229 return False;
232 if (dp->reqpath[0] == '\0') {
233 if (self_referralp) {
234 DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
235 *self_referralp = True;
237 return False;
240 /* check if need to redirect */
241 fstrcpy(localpath, conn->connectpath);
242 fstrcat(localpath, "/");
243 fstrcat(localpath, dp->reqpath);
244 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
245 if (findfirst_flag) {
246 DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
247 "for dfs link %s.\n", dfspath));
248 return False;
249 } else {
250 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
251 dfspath));
252 if (consumedcntp)
253 *consumedcntp = strlen(dfspath);
254 return True;
258 /* redirect if any component in the path is a link */
259 fstrcpy(reqpath, dp->reqpath);
260 p = strrchr(reqpath, '/');
261 while (p) {
262 *p = '\0';
263 fstrcpy(localpath, conn->connectpath);
264 fstrcat(localpath, "/");
265 fstrcat(localpath, reqpath);
266 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
267 DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
269 /* To find the path consumed, we truncate the original
270 DFS pathname passed to use to remove the last
271 component. The length of the resulting string is
272 the path consumed
274 if (consumedcntp) {
275 char *q;
276 pstring buf;
277 pstrcpy(buf, dfspath);
278 trim_char(buf, '\0', '\\');
279 for (; consumed_level; consumed_level--) {
280 q = strrchr(buf, '\\');
281 if (q) *q = 0;
283 *consumedcntp = strlen(buf);
284 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
287 return True;
289 p = strrchr(reqpath, '/');
290 consumed_level++;
293 return False;
296 /*****************************************************************
297 Decides if a dfs pathname should be redirected or not.
298 If not, the pathname is converted to a tcon-relative local unix path
299 *****************************************************************/
300 BOOL dfs_redirect(char* pathname, connection_struct* conn,
301 BOOL findfirst_flag)
303 struct dfs_path dp;
305 if (!conn || !pathname)
306 return False;
308 parse_dfs_path(pathname, &dp);
310 /* if dfs pathname for a non-dfs share, convert to tcon-relative
311 path and return false */
312 if (!lp_msdfs_root(SNUM(conn))) {
313 fstrcpy(pathname, dp.reqpath);
314 return False;
317 if (strcasecmp(dp.servicename, lp_servicename(SNUM(conn)) ) != 0)
318 return False;
320 if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
321 NULL, NULL, NULL, NULL)) {
322 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
323 return True;
324 } else {
325 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
327 /* Form non-dfs tcon-relative path */
328 fstrcpy(pathname, dp.reqpath);
329 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
330 pathname));
331 return False;
333 /* never reached */
334 return False;
337 /**********************************************************************
338 Gets valid referrals for a dfs path and fills up the
339 junction_map structure
340 **********************************************************************/
341 BOOL get_referred_path(char *pathname, struct junction_map* jn,
342 int* consumedcntp, BOOL* self_referralp)
344 struct dfs_path dp;
346 struct connection_struct conns;
347 struct connection_struct* conn = &conns;
348 pstring conn_path;
349 int snum;
350 BOOL ret = False;
352 BOOL self_referral = False;
354 if (!pathname || !jn)
355 return False;
357 if (self_referralp)
358 *self_referralp = False;
359 else
360 self_referralp = &self_referral;
362 parse_dfs_path(pathname, &dp);
364 /* Verify hostname in path */
365 if (local_machine && (strcasecmp(local_machine, dp.hostname) != 0)) {
367 /* Hostname mismatch, check if one of our IP addresses */
368 if (!ismyip(*interpret_addr2(dp.hostname))) {
370 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
371 dp.hostname, pathname));
372 return False;
376 pstrcpy(jn->service_name, dp.servicename);
377 pstrcpy(jn->volume_name, dp.reqpath);
379 /* Verify the share is a dfs root */
380 snum = lp_servicenumber(jn->service_name);
381 if(snum < 0) {
382 if ((snum = find_service(jn->service_name)) < 0)
383 return False;
386 pstrcpy(conn_path, lp_pathname(snum));
387 if (!create_conn_struct(conn, snum, conn_path))
388 return False;
390 if (!lp_msdfs_root(SNUM(conn))) {
391 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
392 dp.servicename, pathname));
393 goto out;
396 if (*lp_msdfs_proxy(snum) != '\0') {
397 struct referral* ref;
398 jn->referral_count = 1;
399 if ((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
400 DEBUG(0, ("malloc failed for referral\n"));
401 goto out;
404 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
405 if (dp.reqpath[0] != '\0')
406 pstrcat(ref->alternate_path, dp.reqpath);
407 ref->proximity = 0;
408 ref->ttl = REFERRAL_TTL;
409 jn->referral_list = ref;
410 if (consumedcntp)
411 *consumedcntp = strlen(pathname);
412 ret = True;
413 goto out;
416 /* If not remote & not a self referral, return False */
417 if (!resolve_dfs_path(pathname, &dp, conn, False,
418 &jn->referral_list, &jn->referral_count,
419 self_referralp, consumedcntp)) {
420 if (!*self_referralp) {
421 DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
422 goto out;
426 /* if self_referral, fill up the junction map */
427 if (*self_referralp) {
428 struct referral* ref;
429 jn->referral_count = 1;
430 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
431 DEBUG(0,("malloc failed for referral\n"));
432 goto out;
435 pstrcpy(ref->alternate_path,pathname);
436 ref->proximity = 0;
437 ref->ttl = REFERRAL_TTL;
438 jn->referral_list = ref;
439 if (consumedcntp)
440 *consumedcntp = strlen(pathname);
443 ret = True;
444 out:
445 talloc_destroy( conn->mem_ctx );
447 return ret;
450 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
451 struct junction_map* junction,
452 int consumedcnt,
453 BOOL self_referral)
455 char* pdata = *ppdata;
457 unsigned char uni_requestedpath[1024];
458 int uni_reqpathoffset1,uni_reqpathoffset2;
459 int uni_curroffset;
460 int requestedpathlen=0;
461 int offset;
462 int reply_size = 0;
463 int i=0;
465 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
467 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
468 STR_TERMINATE);
470 dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
472 DEBUG(10,("ref count = %u\n",junction->referral_count));
474 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
475 VERSION2_REFERRAL_SIZE * junction->referral_count;
477 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
479 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
481 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
482 2 * requestedpathlen;
483 DEBUG(10,("reply_size: %u\n",reply_size));
485 /* add up the unicode lengths of all the referral paths */
486 for(i=0;i<junction->referral_count;i++) {
487 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
488 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
491 DEBUG(10,("reply_size = %u\n",reply_size));
492 /* add the unexplained 0x16 bytes */
493 reply_size += 0x16;
495 pdata = Realloc(pdata,reply_size);
496 if(pdata == NULL) {
497 DEBUG(0,("malloc failed for Realloc!\n"));
498 return -1;
499 } else
500 *ppdata = pdata;
502 /* copy in the dfs requested paths.. required for offset calculations */
503 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
504 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
506 /* create the header */
507 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
508 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
509 if(self_referral)
510 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
511 else
512 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
514 offset = 8;
515 /* add the referral elements */
516 for(i=0;i<junction->referral_count;i++) {
517 struct referral* ref = &(junction->referral_list[i]);
518 int unilen;
520 SSVAL(pdata,offset,2); /* version 2 */
521 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
522 if(self_referral)
523 SSVAL(pdata,offset+4,1);
524 else
525 SSVAL(pdata,offset+4,0);
526 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
527 SIVAL(pdata,offset+8,ref->proximity);
528 SIVAL(pdata,offset+12,ref->ttl);
530 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
531 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
532 /* copy referred path into current offset */
533 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
534 -1, STR_UNICODE);
536 SSVAL(pdata,offset+20,uni_curroffset-offset);
538 uni_curroffset += unilen;
539 offset += VERSION2_REFERRAL_SIZE;
541 /* add in the unexplained 22 (0x16) bytes at the end */
542 memset(pdata+uni_curroffset,'\0',0x16);
543 return reply_size;
546 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
547 struct junction_map* junction,
548 int consumedcnt,
549 BOOL self_referral)
551 char* pdata = *ppdata;
553 unsigned char uni_reqpath[1024];
554 int uni_reqpathoffset1, uni_reqpathoffset2;
555 int uni_curroffset;
556 int reply_size = 0;
558 int reqpathlen = 0;
559 int offset,i=0;
561 DEBUG(10,("setting up version3 referral\n"));
563 reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
565 dump_data(10, (char *) uni_reqpath,reqpathlen);
567 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
568 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
569 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
571 for(i=0;i<junction->referral_count;i++) {
572 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
573 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
576 pdata = Realloc(pdata,reply_size);
577 if(pdata == NULL) {
578 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
579 return -1;
580 } else
581 *ppdata = pdata;
583 /* create the header */
584 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
585 SSVAL(pdata,2,junction->referral_count); /* number of referral */
586 if(self_referral)
587 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
588 else
589 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
591 /* copy in the reqpaths */
592 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
593 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
595 offset = 8;
596 for(i=0;i<junction->referral_count;i++) {
597 struct referral* ref = &(junction->referral_list[i]);
598 int unilen;
600 SSVAL(pdata,offset,3); /* version 3 */
601 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
602 if(self_referral)
603 SSVAL(pdata,offset+4,1);
604 else
605 SSVAL(pdata,offset+4,0);
607 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
608 SIVAL(pdata,offset+8,ref->ttl);
610 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
611 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
612 /* copy referred path into current offset */
613 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
614 -1, STR_UNICODE | STR_TERMINATE);
615 SSVAL(pdata,offset+16,uni_curroffset-offset);
616 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
617 memset(pdata+offset+18,'\0',16);
619 uni_curroffset += unilen;
620 offset += VERSION3_REFERRAL_SIZE;
622 return reply_size;
625 /******************************************************************
626 * Set up the Dfs referral for the dfs pathname
627 ******************************************************************/
629 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
631 struct junction_map junction;
632 int consumedcnt;
633 BOOL self_referral = False;
634 pstring buf;
635 int reply_size = 0;
636 char *pathnamep = pathname;
638 ZERO_STRUCT(junction);
640 /* get the junction entry */
641 if (!pathnamep)
642 return -1;
644 /* Trim pathname sent by client so it begins with only one backslash.
645 Two backslashes confuse some dfs clients
647 while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
648 pathnamep++;
650 pstrcpy(buf, pathnamep);
651 if (!get_referred_path(buf, &junction, &consumedcnt,
652 &self_referral))
653 return -1;
655 if (!self_referral)
657 pathnamep[consumedcnt] = '\0';
659 if( DEBUGLVL( 3 ) ) {
660 int i=0;
661 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
662 for(i=0;i<junction.referral_count;i++)
663 dbgtext(" %s",junction.referral_list[i].alternate_path);
664 dbgtext(".\n");
668 /* create the referral depeding on version */
669 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
670 if(max_referral_level<2 || max_referral_level>3)
671 max_referral_level = 2;
673 switch(max_referral_level) {
674 case 2:
676 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
677 consumedcnt, self_referral);
678 SAFE_FREE(junction.referral_list);
679 break;
681 case 3:
683 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
684 consumedcnt, self_referral);
685 SAFE_FREE(junction.referral_list);
686 break;
688 default:
690 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
691 return -1;
695 DEBUG(10,("DFS Referral pdata:\n"));
696 dump_data(10,*ppdata,reply_size);
697 return reply_size;
700 /**********************************************************************
701 The following functions are called by the NETDFS RPC pipe functions
702 **********************************************************************/
704 /**********************************************************************
705 Creates a junction structure from a Dfs pathname
706 **********************************************************************/
707 BOOL create_junction(char* pathname, struct junction_map* jn)
709 struct dfs_path dp;
711 parse_dfs_path(pathname,&dp);
713 /* check if path is dfs : validate first token */
714 if (local_machine && (strcasecmp(local_machine,dp.hostname)!=0)) {
716 /* Hostname mismatch, check if one of our IP addresses */
717 if (!ismyip(*interpret_addr2(dp.hostname))) {
718 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
719 dp.hostname, pathname));
720 return False;
724 /* Check for a non-DFS share */
725 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
726 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
727 dp.servicename));
728 return False;
731 pstrcpy(jn->service_name,dp.servicename);
732 pstrcpy(jn->volume_name,dp.reqpath);
733 return True;
736 /**********************************************************************
737 Forms a valid Unix pathname from the junction
738 **********************************************************************/
740 static BOOL junction_to_local_path(struct junction_map* jn, char* path,
741 int max_pathlen, connection_struct *conn)
743 int snum;
744 pstring conn_path;
746 if(!path || !jn)
747 return False;
749 snum = lp_servicenumber(jn->service_name);
750 if(snum < 0)
751 return False;
753 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
754 safe_strcat(path, "/", max_pathlen-1);
755 strlower_m(jn->volume_name);
756 safe_strcat(path, jn->volume_name, max_pathlen-1);
758 pstrcpy(conn_path, lp_pathname(snum));
759 if (!create_conn_struct(conn, snum, conn_path))
760 return False;
762 return True;
765 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
767 pstring path;
768 pstring msdfs_link;
769 connection_struct conns;
770 connection_struct *conn = &conns;
771 int i=0;
772 BOOL insert_comma = False;
773 BOOL ret = False;
775 if(!junction_to_local_path(jn, path, sizeof(path), conn))
776 return False;
778 /* form the msdfs_link contents */
779 pstrcpy(msdfs_link, "msdfs:");
780 for(i=0; i<jn->referral_count; i++) {
781 char* refpath = jn->referral_list[i].alternate_path;
783 trim_char(refpath, '\\', '\\');
784 if(*refpath == '\0') {
785 if (i == 0)
786 insert_comma = False;
787 continue;
789 if (i > 0 && insert_comma)
790 pstrcat(msdfs_link, ",");
792 pstrcat(msdfs_link, refpath);
793 if (!insert_comma)
794 insert_comma = True;
798 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
800 if(exists)
801 if(SMB_VFS_UNLINK(conn,path)!=0)
802 goto out;
804 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
805 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
806 path, msdfs_link, strerror(errno)));
807 goto out;
811 ret = True;
813 out:
814 talloc_destroy( conn->mem_ctx );
815 return ret;
818 BOOL remove_msdfs_link(struct junction_map* jn)
820 pstring path;
821 connection_struct conns;
822 connection_struct *conn = &conns;
823 BOOL ret = False;
825 if( junction_to_local_path(jn, path, sizeof(path), conn) ) {
826 if( SMB_VFS_UNLINK(conn, path) == 0 )
827 ret = True;
829 talloc_destroy( conn->mem_ctx );
832 return ret;
835 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
837 int cnt = *jn_count;
838 DIR *dirp;
839 char* dname;
840 pstring connect_path;
841 char* service_name = lp_servicename(snum);
842 connection_struct conns;
843 connection_struct *conn = &conns;
844 struct referral *ref = NULL;
845 BOOL ret = False;
847 pstrcpy(connect_path,lp_pathname(snum));
849 if(*connect_path == '\0')
850 return False;
853 * Fake up a connection struct for the VFS layer.
856 if (!create_conn_struct(conn, snum, connect_path))
857 return False;
859 /* form a junction for the msdfs root - convention
860 DO NOT REMOVE THIS: NT clients will not work with us
861 if this is not present
863 pstrcpy(jn[cnt].service_name, service_name);
864 jn[cnt].volume_name[0] = '\0';
865 jn[cnt].referral_count = 1;
867 ref = jn[cnt].referral_list
868 = (struct referral*) malloc(sizeof(struct referral));
869 if (jn[cnt].referral_list == NULL) {
870 DEBUG(0, ("Malloc failed!\n"));
871 goto out;
874 ref->proximity = 0;
875 ref->ttl = REFERRAL_TTL;
876 if (*lp_msdfs_proxy(snum) != '\0') {
877 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
878 *jn_count = ++cnt;
879 ret = True;
880 goto out;
883 slprintf(ref->alternate_path, sizeof(pstring)-1,
884 "\\\\%s\\%s", local_machine, service_name);
885 cnt++;
887 /* Now enumerate all dfs links */
888 dirp = SMB_VFS_OPENDIR(conn, connect_path);
889 if(!dirp)
890 goto out;
892 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
893 pstring pathreal;
895 pstrcpy(pathreal, connect_path);
896 pstrcat(pathreal, "/");
897 pstrcat(pathreal, dname);
899 if (is_msdfs_link(conn, pathreal, &(jn[cnt].referral_list),
900 &(jn[cnt].referral_count), NULL)) {
901 pstrcpy(jn[cnt].service_name, service_name);
902 pstrcpy(jn[cnt].volume_name, dname);
903 cnt++;
907 SMB_VFS_CLOSEDIR(conn,dirp);
908 *jn_count = cnt;
909 out:
910 talloc_destroy(conn->mem_ctx);
911 return ret;
914 int enum_msdfs_links(struct junction_map* jn)
916 int i=0;
917 int jn_count = 0;
919 if(!lp_host_msdfs())
920 return 0;
922 for(i=0;i < lp_numservices();i++) {
923 if(lp_msdfs_root(i))
924 form_junctions(i,jn,&jn_count);
926 return jn_count;