2890b05b521ad93a86e6783151d80da2769840f2
[ira/wip.git] / source / msdfs / msdfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDfs services for Samba
5    Copyright (C) Shirish Kalele 2000
6
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25 extern pstring global_myname;
26 extern uint32 global_client_caps;
27
28 #ifdef WITH_MSDFS
29
30 /**********************************************************************
31  Create a tcon relative path from a dfs_path structure
32  **********************************************************************/
33
34 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
35 {
36         pstrcpy(pathname,pdp->volumename); 
37         pstrcat(pathname,"\\"); 
38         pstrcat(pathname,pdp->restofthepath); 
39 }
40
41 /**********************************************************************
42   Parse the pathname  of the form \hostname\service\volume\restofthepath
43   into the dfs_path structure 
44  **********************************************************************/
45
46 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
47 {
48         pstring pathname_local;
49         char* p,*temp;
50
51         pstrcpy(pathname_local,pathname);
52         p = temp = pathname_local;
53
54         ZERO_STRUCTP(pdp);
55
56         trim_string(temp,"\\","\\");
57         DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
58
59         /* now tokenize */
60         /* parse out hostname */
61         p = strchr_m(temp,'\\');
62         if(p == NULL)
63                 return False;
64         *p = '\0';
65         pstrcpy(pdp->hostname,temp);
66         DEBUG(10,("hostname: %s\n",pdp->hostname));
67
68         /* parse out servicename */
69         temp = p+1;
70         p = strchr_m(temp,'\\');
71         if(p == NULL) {
72                 pstrcpy(pdp->servicename,temp);
73                 return True;
74         }
75         *p = '\0';
76         pstrcpy(pdp->servicename,temp);
77         DEBUG(10,("servicename: %s\n",pdp->servicename));
78
79         /* parse out volumename */
80         temp = p+1;
81         p = strchr_m(temp,'\\');
82         if(p == NULL) {
83                 pstrcpy(pdp->volumename,temp);
84                 return True;
85         }
86         *p = '\0';
87         pstrcpy(pdp->volumename,temp);
88         DEBUG(10,("volumename: %s\n",pdp->volumename));
89
90         /* remaining path .. */
91         pstrcpy(pdp->restofthepath,p+1);
92         DEBUG(10,("rest of the path: %s\n",pdp->restofthepath));
93         return True;
94 }
95
96 /********************************************************
97  Fake up a connection struct for the VFS layer.
98 *********************************************************/
99
100 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
101 {
102         ZERO_STRUCTP(conn);
103         conn->service = snum;
104         conn->connectpath = path;
105
106         if (!vfs_init(conn)) {
107                 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
108                 return False;
109         }
110         return True;
111 }
112
113 /**********************************************************************
114  Forms a valid Unix pathname from the junction 
115  **********************************************************************/
116
117 static BOOL form_path_from_junction(struct junction_map* jn, char* path, int max_pathlen,
118                                                                 connection_struct *conn)
119 {
120         int snum;
121
122         if(!path || !jn)
123                 return False;
124
125         snum = lp_servicenumber(jn->service_name);
126         if(snum < 0)
127                 return False;
128
129         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
130         safe_strcat(path, "/", max_pathlen-1);
131         strlower(jn->volume_name);
132         safe_strcat(path, jn->volume_name, max_pathlen-1);
133
134         if (!create_conn_struct(conn, snum, path))
135                 return False;
136
137         return True;
138 }
139
140 /**********************************************************************
141  Creates a junction structure from the Dfs pathname
142  **********************************************************************/
143
144 BOOL create_junction(char* pathname, struct junction_map* jn)
145 {
146         struct dfs_path dp;
147   
148         parse_dfs_path(pathname,&dp);
149
150         /* check if path is dfs : check hostname is the first token */
151         if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0)) {
152                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n", dp.hostname, pathname));
153                 return False;
154         }
155
156         /* Check for a non-DFS share */
157         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
158                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
159                 return False;
160         }
161
162         pstrcpy(jn->service_name,dp.servicename);
163         pstrcpy(jn->volume_name,dp.volumename);
164         return True;
165 }
166
167 /**********************************************************************
168  Parse the contents of a symlink to verify if it is an msdfs referral
169  A valid referral is of the form: msdfs:server1\share1,server2\share2
170  **********************************************************************/
171
172 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
173 {
174         pstring temp;
175         char* prot;
176         char* alt_path[MAX_REFERRAL_COUNT];
177         int count=0, i;
178         struct referral* reflist;
179
180         pstrcpy(temp,buf);
181   
182         prot = strtok(temp,":");
183
184         if(!strequal(prot, "msdfs"))
185                 return False;
186
187         /* It's an msdfs referral */
188         if(!preflist) 
189                 return True;
190
191         /* parse out the alternate paths */
192         while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
193                 count++;
194
195         DEBUG(10,("parse_symlink: count=%d\n", count));
196
197         reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
198         if(reflist == NULL) {
199                 DEBUG(0,("parse_symlink: Malloc failed!\n"));
200                 return False;
201         }
202   
203         for(i=0;i<count;i++) {
204                 /* replace / in the alternate path by a \ */
205                 char* p = strchr_m(alt_path[i],'/');
206                 if(p)
207                         *p = '\\'; 
208
209                 pstrcpy(reflist[i].alternate_path, "\\");
210                 pstrcat(reflist[i].alternate_path, alt_path[i]);
211                 reflist[i].proximity = 0;
212                 reflist[i].ttl = REFERRAL_TTL;
213                 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
214         }
215
216         if(refcount)
217                 *refcount = count;
218
219         return True;
220 }
221  
222 /**********************************************************************
223  Returns true if the unix path is a valid msdfs symlink
224  **********************************************************************/
225
226 BOOL is_msdfs_link(connection_struct* conn, char* path)
227 {
228         SMB_STRUCT_STAT st;
229         pstring referral;
230         int referral_len = 0;
231
232         if(!path || !conn)
233                 return False;
234
235         strlower(path);
236
237         if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
238                 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
239                 return False;
240         }
241   
242         if(S_ISLNK(st.st_mode)) {
243                 /* open the link and read it */
244                 referral_len = conn->vfs_ops.readlink(conn, path, referral, sizeof(pstring));
245                 if(referral_len == -1)
246                         DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
247
248                 referral[referral_len] = '\0';
249                 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
250                 if(parse_symlink(referral, NULL, NULL))
251                         return True;
252         }
253         return False;
254 }
255
256 /**********************************************************************
257  Fills in the junction_map struct with the referrals from the 
258  symbolic link
259  **********************************************************************/
260
261 BOOL get_referred_path(struct junction_map* junction)
262 {
263         pstring path;
264         pstring buf;
265         SMB_STRUCT_STAT st;
266         connection_struct conns;
267         connection_struct *conn = &conns;
268  
269         if(!form_path_from_junction(junction, path, sizeof(path), conn))
270                 return False;
271
272         DEBUG(5,("get_referred_path: lstat target: %s\n", path));
273   
274         if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
275                 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
276                 return False;
277         }
278   
279         if(S_ISLNK(st.st_mode)) {
280                 /* open the link and read it to get the dfs referral */
281                 int linkcnt = 0;
282                 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
283                 buf[linkcnt] = '\0';
284                 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
285                 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
286                         return True;
287         }
288         return False;
289 }
290
291 /**************************************************************
292 Decides if given pathname is Dfs and if it should be redirected
293 Converts pathname to non-dfs format if Dfs redirection not required 
294 **************************************************************/
295
296 BOOL dfs_redirect(char* pathname, connection_struct* conn)
297 {
298         struct dfs_path dp;
299         pstring temp;
300         fstring path;
301
302         pstrcpy(temp,pathname);
303
304         if(!lp_msdfs_root(SNUM(conn)) )
305                 return False;
306
307         parse_dfs_path(pathname,&dp);
308
309         if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
310                 return False;
311
312         /* check if need to redirect */
313         fstrcpy(path, conn->connectpath);
314         fstrcat(path, "/");
315         fstrcat(path, dp.volumename);
316         if(is_msdfs_link(conn, path)) {
317                 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
318                 return True;
319         } else {
320                 create_nondfs_path(pathname,&dp);
321                 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
322                                 temp,pathname));
323                 return False;
324         }
325 }
326
327 /*
328   Special DFS redirect call for findfirst's. 
329   If the findfirst is for the dfs junction, then no redirection,
330   if it is for the underlying directory contents, redirect.
331   */
332
333 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
334 {
335         struct dfs_path dp;
336   
337         pstring temp;
338
339         pstrcpy(temp,pathname);
340
341         /* Is the path Dfs-redirectable? */
342         if(!dfs_redirect(temp,conn)) {
343                 pstrcpy(pathname,temp);
344                 return False;
345         }
346
347         parse_dfs_path(pathname,&dp);
348         DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",
349                                 pathname,dp.restofthepath));
350         if(!(*(dp.restofthepath))) {
351                 create_nondfs_path(pathname,&dp);
352                 return False;
353         }
354
355         return True;
356 }
357
358 static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
359                                    struct junction_map* junction,
360                                    BOOL self_referral)
361 {
362         char* pdata = *ppdata;
363
364         unsigned char uni_requestedpath[1024];
365         int uni_reqpathoffset1,uni_reqpathoffset2;
366         int uni_curroffset;
367         int requestedpathlen=0;
368         int offset;
369         int reply_size = 0;
370         int i=0;
371
372         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
373
374         requestedpathlen = (dos_struni2(uni_requestedpath,pathname,512)+1)*2;
375
376         dump_data(10,uni_requestedpath,requestedpathlen);
377
378         DEBUG(10,("ref count = %u\n",junction->referral_count));
379
380         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
381                         VERSION2_REFERRAL_SIZE * junction->referral_count;
382
383         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
384
385         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
386
387         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
388                                         2 * requestedpathlen;
389         DEBUG(10,("reply_size: %u\n",reply_size));
390
391         /* add up the unicode lengths of all the referral paths */
392         for(i=0;i<junction->referral_count;i++) {
393                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
394                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
395         }
396
397         DEBUG(10,("reply_size = %u\n",reply_size));
398         /* add the unexplained 0x16 bytes */
399         reply_size += 0x16;
400
401         pdata = *ppdata = Realloc(pdata,reply_size);
402         if(pdata == NULL) {
403                 DEBUG(0,("malloc failed for Realloc!\n"));
404                 return -1;
405         }
406
407         /* copy in the dfs requested paths.. required for offset calculations */
408         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
409         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
410
411         /* create the header */
412         SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
413         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
414         if(self_referral)
415                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
416         else
417                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
418
419         offset = 8;
420         /* add the referral elements */
421         for(i=0;i<junction->referral_count;i++) {
422                 struct referral* ref = &(junction->referral_list[i]);
423                 int unilen;
424
425                 SSVAL(pdata,offset,2); /* version 2 */
426                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
427                 if(self_referral)
428                         SSVAL(pdata,offset+4,1);
429                 else
430                         SSVAL(pdata,offset+4,0);
431                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
432                 SIVAL(pdata,offset+8,ref->proximity);
433                 SIVAL(pdata,offset+12,ref->ttl);
434
435                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
436                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
437                 /* copy referred path into current offset */
438                 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512) +1)*2;
439                 SSVAL(pdata,offset+20,uni_curroffset-offset);
440
441                 uni_curroffset += unilen;
442                 offset += VERSION2_REFERRAL_SIZE;
443         }
444         /* add in the unexplained 22 (0x16) bytes at the end */
445         memset(pdata+uni_curroffset,'\0',0x16);
446         free(junction->referral_list);
447         return reply_size;
448 }
449
450 static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
451                                    struct junction_map* junction,
452                                    BOOL self_referral)
453 {
454         char* pdata = *ppdata;
455
456         unsigned char uni_reqpath[1024];
457         int uni_reqpathoffset1, uni_reqpathoffset2;
458         int uni_curroffset;
459         int reply_size = 0;
460
461         int reqpathlen = 0;
462         int offset,i=0;
463         
464         DEBUG(10,("setting up version3 referral\n"));
465
466         reqpathlen = (dos_struni2(uni_reqpath,pathname,512)+1)*2;
467         
468         dump_data(10,uni_reqpath,reqpathlen);
469
470         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
471         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
472         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
473
474         for(i=0;i<junction->referral_count;i++) {
475                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
476                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
477         }
478
479         pdata = *ppdata = Realloc(pdata,reply_size);
480         if(pdata == NULL) {
481                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
482                 return -1;
483         }
484         
485         /* create the header */
486         SSVAL(pdata,0,reqpathlen-2); /* path consumed */
487         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
488         if(self_referral)
489                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
490         else
491                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
492         
493         /* copy in the reqpaths */
494         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
495         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
496         
497         offset = 8;
498         for(i=0;i<junction->referral_count;i++) {
499                 struct referral* ref = &(junction->referral_list[i]);
500                 int unilen;
501
502                 SSVAL(pdata,offset,3); /* version 3 */
503                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
504                 if(self_referral)
505                         SSVAL(pdata,offset+4,1);
506                 else
507                         SSVAL(pdata,offset+4,0);
508
509                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
510                 SIVAL(pdata,offset+8,ref->ttl);
511             
512                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
513                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
514                 /* copy referred path into current offset */
515                 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512) +1)*2;
516                 SSVAL(pdata,offset+16,uni_curroffset-offset);
517                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
518                 memset(pdata+offset+18,'\0',16);
519
520                 uni_curroffset += unilen;
521                 offset += VERSION3_REFERRAL_SIZE;
522         }
523         free(junction->referral_list);
524         return reply_size;
525 }
526
527 /******************************************************************
528  * Set up the Dfs referral for the dfs pathname
529  ******************************************************************/
530
531 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
532 {
533         struct junction_map junction;
534
535         BOOL self_referral;
536
537         int reply_size = 0;
538
539         ZERO_STRUCT(junction);
540
541         if(!create_junction(pathname, &junction))
542                 return -1;
543
544         /* get the junction entry */
545         if(!get_referred_path(&junction)) {
546     
547                 /* refer the same pathname, create a standard referral struct */
548                 struct referral* ref;
549                 self_referral = True;
550                 junction.referral_count = 1;
551                 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
552                         DEBUG(0,("malloc failed for referral\n"));
553                         return -1;
554                 }
555       
556                 pstrcpy(ref->alternate_path,pathname);
557                 ref->proximity = 0;
558                 ref->ttl = REFERRAL_TTL;
559                 junction.referral_list = ref;
560         } else {
561                 self_referral = False;
562                 if( DEBUGLVL( 3 ) ) {
563                         int i=0;
564                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathname);
565                         for(i=0;i<junction.referral_count;i++)
566                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
567                         dbgtext(".\n");
568                 }
569         }
570       
571         /* create the referral depeding on version */
572         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
573         if(max_referral_level<2 || max_referral_level>3)
574                 max_referral_level = 2;
575
576         switch(max_referral_level) {
577         case 2:
578                 {
579                 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction, self_referral);
580                 break;
581                 }
582         case 3:
583                 {
584                 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction, self_referral);
585                 break;
586                 }
587         default:
588                 {
589                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
590                 return -1;
591                 }
592         }
593       
594         DEBUG(10,("DFS Referral pdata:\n"));
595         dump_data(10,*ppdata,reply_size);
596         return reply_size;
597 }
598
599 int dfs_path_error(char* inbuf, char* outbuf)
600 {
601         enum remote_arch_types ra_type = get_remote_arch();
602         BOOL NT_arch = ((ra_type==RA_WINNT) || (ra_type == RA_WIN2K));
603         if(NT_arch && (global_client_caps & (CAP_NT_SMBS | CAP_STATUS32)) ) {
604                 SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
605                 return(ERROR(0,0xc0000000|NT_STATUS_PATH_NOT_COVERED));
606         }
607         return(ERROR(ERRSRV,ERRbadpath)); 
608 }
609
610 /**********************************************************************
611  The following functions are called by the NETDFS RPC pipe functions
612  **********************************************************************/
613
614 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
615 {
616         pstring path;
617         pstring msdfs_link;
618         connection_struct conns;
619         connection_struct *conn = &conns;
620         int i=0;
621
622         if(!form_path_from_junction(jn, path, sizeof(path), conn))
623                 return False;
624   
625         /* form the msdfs_link contents */
626         pstrcpy(msdfs_link, "msdfs:");
627         for(i=0; i<jn->referral_count; i++) {
628                 char* refpath = jn->referral_list[i].alternate_path;
629       
630                 trim_string(refpath, "\\", "\\");
631                 if(*refpath == '\0')
632                         continue;
633       
634                 if(i>0)
635                         pstrcat(msdfs_link, ",");
636       
637                 pstrcat(msdfs_link, refpath);
638         }
639
640         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
641
642         if(exists)
643                 if(conn->vfs_ops.unlink(conn,path)!=0)
644                         return False;
645
646         if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
647                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
648                                 path, msdfs_link, strerror(errno)));
649                 return False;
650         }
651         return True;
652 }
653
654 BOOL remove_msdfs_link(struct junction_map* jn)
655 {
656         pstring path;
657         connection_struct conns;
658         connection_struct *conn = &conns;
659
660         if(!form_path_from_junction(jn, path, sizeof(path), conn))
661                 return False;
662      
663         if(conn->vfs_ops.unlink(conn, path)!=0)
664                 return False;
665   
666         return True;
667 }
668
669 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
670 {
671         int cnt = *jn_count;
672         DIR *dirp;
673         char* dname;
674         pstring connect_path;
675         char* service_name = lp_servicename(snum);
676         connection_struct conns;
677         connection_struct *conn = &conns;
678  
679         pstrcpy(connect_path,lp_pathname(snum));
680
681         if(*connect_path == '\0')
682                 return False;
683
684         /*
685          * Fake up a connection struct for the VFS layer.
686          */
687
688         if (!create_conn_struct(conn, snum, connect_path))
689                 return False;
690
691         /* form a junction for the msdfs root - convention */ 
692         /*
693                 pstrpcy(jn[cnt].service_name, service_name);
694                 jn[cnt].volume_name[0] = '\0';
695                 jn[cnt].referral_count = 1;
696   
697                 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
698                 jn[cnt].referral_l
699         */
700
701         dirp = conn->vfs_ops.opendir(conn, connect_path);
702         if(!dirp)
703                 return False;
704
705         while((dname = vfs_readdirname(conn, dirp)) != NULL) {
706                 SMB_STRUCT_STAT st;
707                 pstring pathreal;
708                 fstring buf;
709                 int buflen = 0;
710                 pstrcpy(pathreal, connect_path);
711                 pstrcat(pathreal, "/");
712                 pstrcat(pathreal, dname);
713  
714                 if(conn->vfs_ops.lstat(conn,pathreal,&st) != 0) {
715                         DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
716                         continue;
717                 }
718                 if(S_ISLNK(st.st_mode)) {
719                         buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
720                         buf[buflen] = '\0';
721                         if(parse_symlink(buf, &(jn[cnt].referral_list), &(jn[cnt].referral_count))) {
722                                 pstrcpy(jn[cnt].service_name, service_name);
723                                 pstrcpy(jn[cnt].volume_name, dname);
724                                 cnt++;
725                         }
726                 }
727         }
728   
729         conn->vfs_ops.closedir(conn,dirp);
730         *jn_count = cnt;
731         return True;
732 }
733
734 int enum_msdfs_links(struct junction_map* jn)
735 {
736         int i=0;
737         int jn_count = 0;
738
739         if(!lp_host_msdfs())
740                 return -1;
741
742         for(i=0;*lp_servicename(i);i++) {
743                 if(lp_msdfs_root(i)) 
744                         form_junctions(i,jn,&jn_count);
745         }
746         return jn_count;
747 }
748
749
750 #else
751 /* Stub functions if WITH_MSDFS not defined */
752  int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
753 {
754         return -1;
755 }
756
757  BOOL is_msdfs_link(connection_struct* conn, char* path)
758 {
759         return False;
760 }
761
762 #endif