This commit was generated by cvs2svn to compensate for changes in r30,
[kai/samba-autobuild/.git] / source4 / 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
23 #include "includes.h"
24
25 extern fstring local_machine;
26 extern uint32 global_client_caps;
27 #ifdef 1
28 static uint32 dfs_referral_tries = 0;
29 #endif
30
31 /**********************************************************************
32   Parse the pathname  of the form \hostname\service\reqpath
33   into the dfs_path structure 
34  **********************************************************************/
35
36 static BOOL parse_dfs_path(const char *pathname, struct dfs_path* pdp)
37 {
38         pstring pathname_local;
39         char* p,*temp;
40
41         pstrcpy(pathname_local,pathname);
42         p = temp = pathname_local;
43
44         ZERO_STRUCTP(pdp);
45
46         trim_string(temp,"\\","\\");
47         DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
48
49         /* now tokenize */
50         /* parse out hostname */
51         p = strchr(temp,'\\');
52         if(p == NULL)
53                 return False;
54         *p = '\0';
55         pstrcpy(pdp->hostname,temp);
56         DEBUG(10,("hostname: %s\n",pdp->hostname));
57
58         /* parse out servicename */
59         temp = p+1;
60         p = strchr(temp,'\\');
61         if(p == NULL) {
62                 pstrcpy(pdp->servicename,temp);
63                 pdp->reqpath[0] = '\0';
64                 return True;
65         }
66         *p = '\0';
67         pstrcpy(pdp->servicename,temp);
68         DEBUG(10,("servicename: %s\n",pdp->servicename));
69
70         /* rest is reqpath */
71         pstrcpy(pdp->reqpath, p+1);
72         p = pdp->reqpath;
73         while (*p) {
74                 if (*p == '\\') *p = '/';
75                 p++;
76         }
77
78         DEBUG(10,("rest of the path: %s\n",pdp->reqpath));
79         return True;
80 }
81
82 /********************************************************
83  Fake up a connection struct for the VFS layer.
84 *********************************************************/
85
86 static BOOL create_conn_struct( struct tcon_context *conn, int snum, char *path)
87 {
88         ZERO_STRUCTP(conn);
89         conn->service = snum;
90         conn->connectpath = path;
91         pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
92
93         if (!smbd_vfs_init(conn)) {
94                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
95                 return False;
96         }
97         return True;
98 }
99
100
101 /**********************************************************************
102  Parse the contents of a symlink to verify if it is an msdfs referral
103  A valid referral is of the form: msdfs:server1\share1,server2\share2
104  **********************************************************************/
105 static BOOL parse_symlink(char* buf,struct referral** preflist, 
106                                  int* refcount)
107 {
108         pstring temp;
109         char* prot;
110         char* alt_path[MAX_REFERRAL_COUNT];
111         int count=0, i;
112         struct referral* reflist;
113
114         pstrcpy(temp,buf);
115   
116         prot = strtok(temp,":");
117
118         if (!strequal(prot, "msdfs"))
119                 return False;
120
121         /* No referral list requested. Just yes/no. */
122         if (!preflist) 
123                 return True;
124
125         /* parse out the alternate paths */
126         while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
127                 count++;
128
129         DEBUG(10,("parse_symlink: count=%d\n", count));
130
131         reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
132         if(reflist == NULL) {
133                 DEBUG(0,("parse_symlink: Malloc failed!\n"));
134                 return False;
135         }
136         
137         for(i=0;i<count;i++) {
138                 /* replace / in the alternate path by a \ */
139                 char* p = strchr(alt_path[i],'/');
140                 int j = i;
141 #ifdef 1
142                 // used for testing splitting files between servers
143                 if ((count == 2) &&
144                         (dfs_referral_tries & 0xfffffffe)    // this will reverse the order of the list
145                         j = count - 1;
146                 dfs_referral_tries++;
147 #endif                                  
148                 if(p)
149                         *p = '\\'; 
150
151                 pstrcpy(reflist[j].alternate_path, "\\");
152                 pstrcat(reflist[j].alternate_path, alt_path[i]);
153                 reflist[j].proximity = 0;
154                 reflist[j].ttl = REFERRAL_TTL;
155                 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[j].alternate_path));
156         }
157
158         if(refcount)
159                 *refcount = count;
160
161         return True;
162 }
163  
164 /**********************************************************************
165  Returns true if the unix path is a valid msdfs symlink
166  **********************************************************************/
167 BOOL is_msdfs_link(struct tcon_context *conn, char* path,
168                    struct referral** reflistp, int* refcnt,
169                    SMB_STRUCT_STAT *sbufp)
170 {
171         SMB_STRUCT_STAT st;
172         pstring referral;
173         int referral_len = 0;
174
175         if (!path || !conn)
176                 return False;
177
178         strlower(path);
179
180         if (sbufp == NULL)
181                 sbufp = &st;
182
183         if (conn->vfs_ops.lstat(conn, path, sbufp) != 0) {
184                 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
185                 return False;
186         }
187   
188         if (S_ISLNK(sbufp->st_mode)) {
189                 /* open the link and read it */
190                 referral_len = conn->vfs_ops.readlink(conn, path, referral, 
191                                                       sizeof(pstring));
192                 if (referral_len == -1) {
193                         DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
194                         return False;
195                 }
196
197                 referral[referral_len] = '\0';
198                 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
199                 if (parse_symlink(referral, reflistp, refcnt))
200                         return True;
201         }
202         return False;
203 }
204
205 /*****************************************************************
206  Used by other functions to decide if a dfs path is remote,
207 and to get the list of referred locations for that remote path.
208  
209 findfirst_flag: For findfirsts, dfs links themselves are not
210 redirected, but paths beyond the links are. For normal smb calls,
211 even dfs links need to be redirected.
212
213 self_referralp: clients expect a dfs referral for the same share when
214 they request referrals for dfs roots on a server. 
215
216 consumedcntp: how much of the dfs path is being redirected. the client
217 should try the remaining path on the redirected server.
218 *****************************************************************/
219 static BOOL resolve_dfs_path(char* dfspath, struct dfs_path* dp, 
220                       struct tcon_context *conn,
221                       BOOL findfirst_flag,
222                       struct referral** reflistpp, int* refcntp,
223                       BOOL* self_referralp, int* consumedcntp)
224 {
225         fstring localpath;
226
227         char *p;
228         fstring reqpath;
229
230         if (!dp || !conn) {
231                 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL tcon_context!\n"));
232                 return False;
233         }
234
235         if (dp->reqpath[0] == '\0') {
236                 if (self_referralp) {
237                         DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
238                         *self_referralp = True;
239                 }
240                 return False;
241         }
242
243         /* check if need to redirect */
244         fstrcpy(localpath, conn->connectpath);
245         fstrcat(localpath, "/");
246         fstrcat(localpath, dp->reqpath);
247         if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL))
248         {
249                 if (findfirst_flag) {
250                         DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
251                                  "for dfs link %s.\n", dfspath));
252                         return False;
253                 } else {                
254                         DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
255                                  dfspath));
256                         if (consumedcntp) 
257                                 *consumedcntp = strlen(dfspath);
258                         return True;
259                 }
260         } 
261
262         /* also redirect if the parent directory is a dfs link */
263         fstrcpy(reqpath, dp->reqpath);
264         p = strrchr(reqpath, '/');
265         if (p) {
266                 *p = '\0';
267                 fstrcpy(localpath, conn->connectpath);
268                 fstrcat(localpath, "/");
269                 fstrcat(localpath, reqpath);
270                 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
271                         DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
272
273                         /* To find the path consumed, we truncate the original
274                            DFS pathname passed to use to remove the last
275                            component. The length of the resulting string is
276                            the path consumed 
277                         */
278                         if (consumedcntp) {
279                                 char *q;
280                                 pstring buf;
281                                 pstrcpy(buf, dfspath);
282                                 trim_string(buf, NULL, "\\");
283                                 q = strrchr(buf, '\\');
284                                 if (q) 
285                                         *q = '\0';
286                                 *consumedcntp = strlen(buf);
287                                 DEBUG(10, ("resolve_dfs_path: Path consumed: %d\n", *consumedcntp));
288                         }
289                         
290                         return True;
291                 }
292         }
293         
294         return False;
295 }
296
297 /*****************************************************************
298   Decides if a dfs pathname should be redirected or not.
299   If not, the pathname is converted to a tcon-relative local unix path
300 *****************************************************************/
301 BOOL dfs_redirect(const char *pathname, struct tcon_context *conn,
302                   BOOL findfirst_flag)
303 {
304         struct dfs_path dp;
305         
306         if (!conn || !pathname)
307                 return False;
308
309         parse_dfs_path(pathname, &dp);
310
311         /* if dfs pathname for a non-dfs share, convert to tcon-relative
312            path and return false */
313         if (!lp_msdfs_root(SNUM(conn))) {
314                 fstrcpy(pathname, dp.reqpath);
315                 return False;
316         }
317         
318         if (strcasecmp(dp.servicename, lp_servicename(SNUM(conn)) ) != 0) 
319                 return False;
320
321         if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
322                              NULL, NULL, NULL, NULL)) {
323                 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
324                 return True;
325         } else {
326                 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
327                 
328                 /* Form non-dfs tcon-relative path */
329                 fstrcpy(pathname, dp.reqpath);
330                 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
331                          pathname));
332                 return False;
333         }
334         /* never reached */
335         return False;
336 }
337
338 /**********************************************************************
339  Gets valid referrals for a dfs path and fills up the
340  junction_map structure
341  **********************************************************************/
342 BOOL get_referred_path(char *pathname, struct junction_map* jn,
343                        int* consumedcntp, BOOL* self_referralp)
344 {
345         struct dfs_path dp;
346
347         struct tcon_context conns;
348         struct tcon_context *conn = &conns;
349         pstring conn_path;
350         int snum;
351
352         BOOL self_referral = False;
353
354         if (!pathname || !jn)
355                 return False;
356
357         if (self_referralp)
358                 *self_referralp = False;
359         else
360                 self_referralp = &self_referral;
361
362         parse_dfs_path(pathname, &dp);
363
364         /* Verify hostname in path */
365         if (local_machine && (strcasecmp(local_machine, dp.hostname) != 0)) {
366
367            /* Hostname mismatch, check if one of our IP addresses */
368            if (!ismyip(*interpret_addr2(dp.hostname))) {
369                 
370                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
371                           dp.hostname, pathname));
372                 return False;
373            }
374         }
375
376         pstrcpy(jn->service_name, dp.servicename);
377         pstrcpy(jn->volume_name, dp.reqpath);
378
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;
384         }
385         
386         pstrcpy(conn_path, lp_pathname(snum));
387         if (!create_conn_struct(conn, snum, conn_path))
388                 return False;
389         
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                 return False;
394         }
395
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)))
400                     == NULL) {
401                         DEBUG(0, ("malloc failed for referral\n"));
402                         return False;
403                 }
404
405                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
406                 if (dp.reqpath[0] != '\0')
407                         pstrcat(ref->alternate_path, dp.reqpath);
408                 ref->proximity = 0;
409                 ref->ttl = REFERRAL_TTL;
410                 jn->referral_list = ref;
411                 if (consumedcntp)
412                         *consumedcntp = strlen(pathname);
413                 return True;
414         }
415
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                         return False;
423                 }
424         }
425         
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)))
431                    == NULL) {
432                         DEBUG(0,("malloc failed for referral\n"));
433                         return False;
434                 }
435       
436                 pstrcpy(ref->alternate_path,pathname);
437                 ref->proximity = 0;
438                 ref->ttl = REFERRAL_TTL;
439                 jn->referral_list = ref;
440                 if (consumedcntp)
441                         *consumedcntp = strlen(pathname);
442         }
443
444         return True;
445 }
446
447 static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
448                                    struct junction_map* junction,
449                                    int consumedcnt,
450                                    BOOL self_referral)
451 {
452         char* pdata = *ppdata;
453
454         unsigned char uni_requestedpath[1024];
455         int uni_reqpathoffset1,uni_reqpathoffset2;
456         int uni_curroffset;
457         int requestedpathlen=0;
458         int offset;
459         int reply_size = 0;
460         int i=0;
461
462         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
463
464         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
465                                        STR_TERMINATE);
466
467         dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
468
469         DEBUG(10,("ref count = %u\n",junction->referral_count));
470
471         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
472                         VERSION2_REFERRAL_SIZE * junction->referral_count;
473
474         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
475
476         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
477
478         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
479                                         2 * requestedpathlen;
480         DEBUG(10,("reply_size: %u\n",reply_size));
481
482         /* add up the unicode lengths of all the referral paths */
483         for(i=0;i<junction->referral_count;i++) {
484                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
485                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
486         }
487
488         DEBUG(10,("reply_size = %u\n",reply_size));
489         /* add the unexplained 0x16 bytes */
490         reply_size += 0x16;
491
492         pdata = Realloc(pdata,reply_size);
493         if(pdata == NULL) {
494                 DEBUG(0,("malloc failed for Realloc!\n"));
495                 return -1;
496         } else
497                 *ppdata = pdata;
498
499         /* copy in the dfs requested paths.. required for offset calculations */
500         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
501         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
502
503         /* create the header */
504         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
505         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
506         if(self_referral)
507                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
508         else
509                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
510
511         offset = 8;
512         /* add the referral elements */
513         for(i=0;i<junction->referral_count;i++) {
514                 struct referral* ref = &(junction->referral_list[i]);
515                 int unilen;
516
517                 SSVAL(pdata,offset,2); /* version 2 */
518                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
519                 if(self_referral)
520                         SSVAL(pdata,offset+4,1);
521                 else
522                         SSVAL(pdata,offset+4,0);
523                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
524                 SIVAL(pdata,offset+8,ref->proximity);
525                 SIVAL(pdata,offset+12,ref->ttl);
526
527                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
528                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
529                 /* copy referred path into current offset */
530                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
531                                      -1, STR_UNICODE);
532
533                 SSVAL(pdata,offset+20,uni_curroffset-offset);
534
535                 uni_curroffset += unilen;
536                 offset += VERSION2_REFERRAL_SIZE;
537         }
538         /* add in the unexplained 22 (0x16) bytes at the end */
539         memset(pdata+uni_curroffset,'\0',0x16);
540         return reply_size;
541 }
542
543 static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
544                                    struct junction_map* junction,
545                                    int consumedcnt,
546                                    BOOL self_referral)
547 {
548         char* pdata = *ppdata;
549
550         unsigned char uni_reqpath[1024];
551         int uni_reqpathoffset1, uni_reqpathoffset2;
552         int uni_curroffset;
553         int reply_size = 0;
554
555         int reqpathlen = 0;
556         int offset,i=0;
557         
558         DEBUG(10,("setting up version3 referral\n"));
559
560         reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
561         
562         dump_data(10, (char *) uni_reqpath,reqpathlen);
563
564         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
565         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
566         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
567
568         for(i=0;i<junction->referral_count;i++) {
569                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
570                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
571         }
572
573         pdata = Realloc(pdata,reply_size);
574         if(pdata == NULL) {
575                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
576                 return -1;
577         } else
578                 *ppdata = pdata;
579
580         /* create the header */
581         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
582         SSVAL(pdata,2,junction->referral_count); /* number of referral */
583         if(self_referral)
584                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
585         else
586                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
587         
588         /* copy in the reqpaths */
589         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
590         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
591         
592         offset = 8;
593         for(i=0;i<junction->referral_count;i++) {
594                 struct referral* ref = &(junction->referral_list[i]);
595                 int unilen;
596
597                 SSVAL(pdata,offset,3); /* version 3 */
598                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
599                 if(self_referral)
600                         SSVAL(pdata,offset+4,1);
601                 else
602                         SSVAL(pdata,offset+4,0);
603
604                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
605                 SIVAL(pdata,offset+8,ref->ttl);
606             
607                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
608                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
609                 /* copy referred path into current offset */
610                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
611                                      -1, STR_UNICODE | STR_TERMINATE);
612                 SSVAL(pdata,offset+16,uni_curroffset-offset);
613                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
614                 memset(pdata+offset+18,'\0',16);
615
616                 uni_curroffset += unilen;
617                 offset += VERSION3_REFERRAL_SIZE;
618         }
619         return reply_size;
620 }
621
622 /******************************************************************
623  * Set up the Dfs referral for the dfs pathname
624  ******************************************************************/
625
626 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
627 {
628         struct junction_map junction;
629         int consumedcnt;
630         BOOL self_referral = False;
631         pstring buf;
632         int reply_size = 0;
633         char *pathnamep = pathname;
634
635         ZERO_STRUCT(junction);
636
637         /* get the junction entry */
638         if (!pathnamep)
639                 return -1;
640
641         /* Trim pathname sent by client so it begins with only one backslash.
642            Two backslashes confuse some dfs clients
643          */
644         while (strlen(pathnamep) > 1 && pathnamep[0] == '\\'
645                && pathnamep[1] == '\\')
646                 pathnamep++;
647
648         pstrcpy(buf, pathnamep);
649         if (!get_referred_path(buf, &junction, &consumedcnt,
650                                &self_referral))
651                 return -1;
652         
653         if (!self_referral)
654         {
655                 pathnamep[consumedcnt] = '\0';
656
657                 if( DEBUGLVL( 3 ) ) {
658                         int i=0;
659                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
660                         for(i=0;i<junction.referral_count;i++)
661                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
662                         dbgtext(".\n");
663                 }
664         }
665
666         /* create the referral depeding on version */
667         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
668         if(max_referral_level<2 || max_referral_level>3)
669                 max_referral_level = 2;
670
671         switch(max_referral_level) {
672         case 2:
673                 {
674                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
675                                                      consumedcnt, self_referral);
676                 SAFE_FREE(junction.referral_list);
677                 break;
678                 }
679         case 3:
680                 {
681                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
682                                                      consumedcnt, self_referral);
683                 SAFE_FREE(junction.referral_list);
684                 break;
685                 }
686         default:
687                 {
688                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
689                 return -1;
690                 }
691         }
692       
693         DEBUG(10,("DFS Referral pdata:\n"));
694         dump_data(10,*ppdata,reply_size);
695         return reply_size;
696 }
697
698 /**********************************************************************
699  The following functions are called by the NETDFS RPC pipe functions
700  **********************************************************************/
701
702 /**********************************************************************
703  Creates a junction structure from a Dfs pathname
704  **********************************************************************/
705 BOOL create_junction(char* pathname, struct junction_map* jn)
706 {
707         struct dfs_path dp;
708  
709         parse_dfs_path(pathname,&dp);
710
711         /* check if path is dfs : validate first token */
712         if (local_machine && (strcasecmp(local_machine,dp.hostname)!=0)) {
713             
714            /* Hostname mismatch, check if one of our IP addresses */
715            if (!ismyip(*interpret_addr2(dp.hostname))) {
716                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
717                          dp.hostname, pathname));
718                 return False;
719            }
720         }
721
722         /* Check for a non-DFS share */
723         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
724                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", 
725                          dp.servicename));
726                 return False;
727         }
728
729         pstrcpy(jn->service_name,dp.servicename);
730         pstrcpy(jn->volume_name,dp.reqpath);
731         return True;
732 }
733
734 /**********************************************************************
735  Forms a valid Unix pathname from the junction 
736  **********************************************************************/
737
738 static BOOL junction_to_local_path(struct junction_map* jn, char* path,
739                                    int max_pathlen, struct tcon_context *conn)
740 {
741         int snum;
742         pstring conn_path;
743
744         if(!path || !jn)
745                 return False;
746
747         snum = lp_servicenumber(jn->service_name);
748         if(snum < 0)
749                 return False;
750
751         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
752         safe_strcat(path, "/", max_pathlen-1);
753         strlower(jn->volume_name);
754         safe_strcat(path, jn->volume_name, max_pathlen-1);
755
756         pstrcpy(conn_path, lp_pathname(snum));
757         if (!create_conn_struct(conn, snum, conn_path))
758                 return False;
759
760         return True;
761 }
762
763 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
764 {
765         pstring path;
766         pstring msdfs_link;
767         struct tcon_context conns;
768         struct tcon_context *conn = &conns;
769         int i=0;
770         BOOL insert_comma = False;
771
772         if(!junction_to_local_path(jn, path, sizeof(path), conn))
773                 return False;
774   
775         /* form the msdfs_link contents */
776         pstrcpy(msdfs_link, "msdfs:");
777         for(i=0; i<jn->referral_count; i++) {
778                 char* refpath = jn->referral_list[i].alternate_path;
779       
780                 trim_string(refpath, "\\", "\\");
781                 if(*refpath == '\0') {
782                         if (i == 0)
783                                 insert_comma = False;
784                         continue;
785                 }
786                 if (i > 0 && insert_comma)
787                         pstrcat(msdfs_link, ",");
788
789                 pstrcat(msdfs_link, refpath);
790                 if (!insert_comma)
791                         insert_comma = True;
792                 
793         }
794
795         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
796
797         if(exists)
798                 if(conn->vfs_ops.unlink(conn,path)!=0)
799                         return False;
800
801         if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
802                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
803                                 path, msdfs_link, strerror(errno)));
804                 return False;
805         }
806         return True;
807 }
808
809 BOOL remove_msdfs_link(struct junction_map* jn)
810 {
811         pstring path;
812         struct tcon_context conns;
813         struct tcon_context *conn = &conns;
814
815         if(!junction_to_local_path(jn, path, sizeof(path), conn))
816                 return False;
817      
818         if(conn->vfs_ops.unlink(conn, path)!=0)
819                 return False;
820   
821         return True;
822 }
823
824 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
825 {
826         int cnt = *jn_count;
827         DIR *dirp;
828         char* dname;
829         pstring connect_path;
830         char* service_name = lp_servicename(snum);
831         struct tcon_context conns;
832         struct tcon_context *conn = &conns;
833         struct referral *ref = NULL;
834  
835         pstrcpy(connect_path,lp_pathname(snum));
836
837         if(*connect_path == '\0')
838                 return False;
839
840         /*
841          * Fake up a connection struct for the VFS layer.
842          */
843
844         if (!create_conn_struct(conn, snum, connect_path))
845                 return False;
846
847         /* form a junction for the msdfs root - convention 
848            DO NOT REMOVE THIS: NT clients will not work with us
849            if this is not present
850         */ 
851         pstrcpy(jn[cnt].service_name, service_name);
852         jn[cnt].volume_name[0] = '\0';
853         jn[cnt].referral_count = 1;
854
855         ref = jn[cnt].referral_list
856                 = (struct referral*) malloc(sizeof(struct referral));
857         if (jn[cnt].referral_list == NULL) {
858                 DEBUG(0, ("Malloc failed!\n"));
859                 return False;
860         }
861
862         ref->proximity = 0;
863         ref->ttl = REFERRAL_TTL;
864         if (*lp_msdfs_proxy(snum) != '\0') {
865                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
866                 *jn_count = ++cnt;
867                 return True;
868         }
869                 
870         slprintf(ref->alternate_path, sizeof(pstring)-1,
871                  "\\\\%s\\%s", local_machine, service_name);
872         cnt++;
873         
874         /* Now enumerate all dfs links */
875         dirp = conn->vfs_ops.opendir(conn, connect_path);
876         if(!dirp)
877                 return False;
878
879         while((dname = vfs_readdirname(conn, dirp)) != NULL) {
880                 pstring pathreal;
881
882                 pstrcpy(pathreal, connect_path);
883                 pstrcat(pathreal, "/");
884                 pstrcat(pathreal, dname);
885  
886                 if (is_msdfs_link(conn, pathreal, &(jn[cnt].referral_list),
887                                   &(jn[cnt].referral_count), NULL)) {
888                         pstrcpy(jn[cnt].service_name, service_name);
889                         pstrcpy(jn[cnt].volume_name, dname);
890                         cnt++;
891                 }
892         }
893         
894         conn->vfs_ops.closedir(conn,dirp);
895         *jn_count = cnt;
896         return True;
897 }
898
899 int enum_msdfs_links(struct junction_map* jn)
900 {
901         int i=0;
902         int jn_count = 0;
903
904         if(!lp_host_msdfs())
905                 return -1;
906
907         for(i=0;*lp_servicename(i);i++) {
908                 if(lp_msdfs_root(i)) 
909                         form_junctions(i,jn,&jn_count);
910         }
911         return jn_count;
912 }
913