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