2 Unix SMB/Netbios implementation.
4 MSDfs services for Samba
5 Copyright (C) Shirish Kalele 2000
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 extern int DEBUGLEVEL;
25 extern pstring global_myname;
26 extern uint32 global_client_caps;
30 /**********************************************************************
31 Create a tcon relative path from a dfs_path structure
32 **********************************************************************/
33 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
35 pstrcpy(pathname,pdp->volumename);
36 pstrcat(pathname,"\\");
37 pstrcat(pathname,pdp->restofthepath);
40 /**********************************************************************
41 Parse the pathname of the form \hostname\service\volume\restofthepath
42 into the dfs_path structure
43 **********************************************************************/
44 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
46 pstring pathname_local;
49 pstrcpy(pathname_local,pathname);
50 p = temp = pathname_local;
54 trim_string(temp,"\\","\\");
55 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
58 /* parse out hostname */
59 p = strchr(temp,'\\');
63 pstrcpy(pdp->hostname,temp);
64 DEBUG(10,("hostname: %s\n",pdp->hostname));
66 /* parse out servicename */
68 p = strchr(temp,'\\');
71 pstrcpy(pdp->servicename,temp);
75 pstrcpy(pdp->servicename,temp);
76 DEBUG(10,("servicename: %s\n",pdp->servicename));
78 /* parse out volumename */
80 p = strchr(temp,'\\');
83 pstrcpy(pdp->volumename,temp);
87 pstrcpy(pdp->volumename,temp);
88 DEBUG(10,("volumename: %s\n",pdp->volumename));
90 /* remaining path .. */
91 pstrcpy(pdp->restofthepath,p+1);
92 DEBUG(10,("rest of the path: %s\n",pdp->restofthepath));
96 /**********************************************************************
97 Forms a valid Unix pathname from the junction
98 **********************************************************************/
99 static BOOL form_path_from_junction(struct junction_map* jn, char* path,
107 snum = lp_servicenumber(jn->service_name);
111 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
112 safe_strcat(path, "/", max_pathlen-1);
113 strlower(jn->volume_name);
114 safe_strcat(path, jn->volume_name, max_pathlen-1);
118 /**********************************************************************
119 Creates a junction structure from the Dfs pathname
120 **********************************************************************/
121 BOOL create_junction(char* pathname, struct junction_map* jn)
125 parse_dfs_path(pathname,&dp);
127 /* check if path is dfs : check hostname is the first token */
128 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
130 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
131 dp.hostname, pathname));
135 /* Check for a non-DFS share */
136 if(!lp_msdfs_root(lp_servicenumber(dp.servicename)))
138 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
143 pstrcpy(jn->service_name,dp.servicename);
144 pstrcpy(jn->volume_name,dp.volumename);
148 /**********************************************************************
149 Parse the contents of a symlink to verify if it is an msdfs referral
150 A valid referral is of the form: msdfs:server1\share1,server2\share2
151 **********************************************************************/
152 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
156 char* alt_path[MAX_REFERRAL_COUNT];
158 struct referral* reflist;
162 prot = strtok(temp,":");
164 if(!strequal(prot, "msdfs"))
167 /* It's an msdfs referral */
171 /* parse out the alternate paths */
172 while(((alt_path[count] = strtok(NULL,",")) != NULL)
173 && count<MAX_REFERRAL_COUNT)
176 DEBUG(10,("parse_symlink: count=%d\n", count));
177 reflist = *preflist = (struct referral*) malloc(count *
178 sizeof(struct referral));
181 DEBUG(0,("parse_symlink: Malloc failed!\n"));
187 /* replace / in the alternate path by a \ */
188 char* p = strchr(alt_path[i],'/');
191 pstrcpy(reflist[i].alternate_path, "\\");
192 pstrcat(reflist[i].alternate_path, alt_path[i]);
193 reflist[i].proximity = 0;
194 reflist[i].ttl = REFERRAL_TTL;
195 DEBUG(10, ("parse_symlink: Created alt path: %s\n",
196 reflist[i].alternate_path));
205 /**********************************************************************
206 Returns true if the unix path is a valid msdfs symlink
207 **********************************************************************/
208 BOOL is_msdfs_link(connection_struct* conn, char* path)
212 int referral_len = 0;
219 if(conn->vfs_ops.lstat(conn,dos_to_unix(path,False),&st) != 0)
221 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
225 if(S_ISLNK(st.st_mode))
227 /* open the link and read it */
228 referral_len = readlink(path, referral, sizeof(pstring));
229 if(referral_len == -1)
230 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
231 path, strerror(errno)));
233 referral[referral_len] = '\0';
234 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
235 if(parse_symlink(referral, NULL, NULL))
241 /**********************************************************************
242 Fills in the junction_map struct with the referrals from the
244 **********************************************************************/
245 BOOL get_referred_path(struct junction_map* junction)
251 if(!form_path_from_junction(junction, path, sizeof(path)))
254 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
256 if(lstat(dos_to_unix(path, False),&st) != 0)
258 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
262 if(S_ISLNK(st.st_mode))
264 /* open the link and read it to get the dfs referral */
266 linkcnt = readlink(path, buf, sizeof(buf));
268 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
269 if(parse_symlink(buf, &junction->referral_list,
270 &junction->referral_count))
276 /**************************************************************
277 Decides if given pathname is Dfs and if it should be redirected
278 Converts pathname to non-dfs format if Dfs redirection not required
279 **************************************************************/
280 BOOL dfs_redirect(char* pathname, connection_struct* conn)
286 pstrcpy(temp,pathname);
288 if(!lp_msdfs_root(SNUM(conn)) )
291 parse_dfs_path(pathname,&dp);
293 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
296 /* check if need to redirect */
297 fstrcpy(path, conn->connectpath);
299 fstrcat(path, dp.volumename);
300 if(is_msdfs_link(conn, path))
302 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
307 create_nondfs_path(pathname,&dp);
308 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
315 Special DFS redirect call for findfirst's.
316 If the findfirst is for the dfs junction, then no redirection,
317 if it is for the underlying directory contents, redirect.
319 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
325 pstrcpy(temp,pathname);
327 /* Is the path Dfs-redirectable? */
328 if(!dfs_redirect(temp,conn))
330 pstrcpy(pathname,temp);
334 parse_dfs_path(pathname,&dp);
335 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",pathname,dp.restofthepath));
336 if(*(dp.restofthepath))
340 create_nondfs_path(pathname,&dp);
345 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
346 struct junction_map* junction,
349 char* pdata = *ppdata;
351 unsigned char uni_requestedpath[1024];
352 int uni_reqpathoffset1,uni_reqpathoffset2;
354 int requestedpathlen=0;
359 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
361 requestedpathlen = (dos_struni2(uni_requestedpath,pathname,512)+1)*2;
363 dump_data(10,uni_requestedpath,requestedpathlen);
365 DEBUG(10,("ref count = %u\n",junction->referral_count));
367 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
368 VERSION2_REFERRAL_SIZE * junction->referral_count;
370 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
372 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
374 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
375 2 * requestedpathlen;
376 DEBUG(10,("reply_size: %u\n",reply_size));
378 /* add up the unicode lengths of all the referral paths */
379 for(i=0;i<junction->referral_count;i++)
381 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
382 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
385 DEBUG(10,("reply_size = %u\n",reply_size));
386 /* add the unexplained 0x16 bytes */
389 pdata = *ppdata = Realloc(pdata,reply_size);
392 DEBUG(0,("malloc failed for Realloc!\n"));
396 /* copy in the dfs requested paths.. required for offset calculations */
397 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
398 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
401 /* create the header */
402 SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
403 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
405 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
407 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
410 /* add the referral elements */
411 for(i=0;i<junction->referral_count;i++)
413 struct referral* ref = &(junction->referral_list[i]);
416 SSVAL(pdata,offset,2); /* version 2 */
417 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
419 SSVAL(pdata,offset+4,1);
421 SSVAL(pdata,offset+4,0);
422 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
423 SIVAL(pdata,offset+8,ref->proximity);
424 SIVAL(pdata,offset+12,ref->ttl);
426 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
427 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
428 /* copy referred path into current offset */
429 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512)
431 SSVAL(pdata,offset+20,uni_curroffset-offset);
433 uni_curroffset += unilen;
434 offset += VERSION2_REFERRAL_SIZE;
436 /* add in the unexplained 22 (0x16) bytes at the end */
437 memset(pdata+uni_curroffset,'\0',0x16);
438 free(junction->referral_list);
442 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
443 struct junction_map* junction,
446 char* pdata = *ppdata;
448 unsigned char uni_reqpath[1024];
449 int uni_reqpathoffset1, uni_reqpathoffset2;
456 DEBUG(10,("setting up version3 referral\n"));
458 reqpathlen = (dos_struni2(uni_reqpath,pathname,512)+1)*2;
460 dump_data(10,uni_reqpath,reqpathlen);
462 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE *
463 junction->referral_count;
464 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
465 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
467 for(i=0;i<junction->referral_count;i++)
469 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
470 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
473 pdata = *ppdata = Realloc(pdata,reply_size);
476 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
480 /* create the header */
481 SSVAL(pdata,0,reqpathlen-2); /* path consumed */
482 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
484 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
486 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
488 /* copy in the reqpaths */
489 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
490 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
493 for(i=0;i<junction->referral_count;i++)
495 struct referral* ref = &(junction->referral_list[i]);
498 SSVAL(pdata,offset,3); /* version 3 */
499 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
501 SSVAL(pdata,offset+4,1);
503 SSVAL(pdata,offset+4,0);
505 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
506 SIVAL(pdata,offset+8,ref->ttl);
508 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
509 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
510 /* copy referred path into current offset */
511 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512)
513 SSVAL(pdata,offset+16,uni_curroffset-offset);
514 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
515 memset(pdata+offset+18,'\0',16);
517 uni_curroffset += unilen;
518 offset += VERSION3_REFERRAL_SIZE;
520 free(junction->referral_list);
526 /******************************************************************
527 * Set up the Dfs referral for the dfs pathname
528 ******************************************************************/
529 int setup_dfs_referral(char* pathname, int max_referral_level,
532 struct junction_map junction;
538 ZERO_STRUCT(junction);
540 if(!create_junction(pathname, &junction))
543 /* get the junction entry */
544 if(!get_referred_path(&junction))
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)
553 DEBUG(0,("malloc failed for referral\n"));
557 pstrcpy(ref->alternate_path,pathname);
559 ref->ttl = REFERRAL_TTL;
560 junction.referral_list = ref;
564 self_referral = False;
568 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathname);
569 for(i=0;i<junction.referral_count;i++)
570 dbgtext(" %s",junction.referral_list[i].alternate_path);
575 /* create the referral depeding on version */
576 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
577 if(max_referral_level<2 || max_referral_level>3) max_referral_level = 2;
579 switch(max_referral_level)
583 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction,
589 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction,
595 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n",
596 max_referral_level));
601 DEBUG(10,("DFS Referral pdata:\n"));
602 dump_data(10,*ppdata,reply_size);
606 int dfs_path_error(char* inbuf, char* outbuf)
608 enum remote_arch_types ra_type = get_remote_arch();
609 BOOL NT_arch = ((ra_type==RA_WINNT) || (ra_type == RA_WIN2K));
610 if(NT_arch && (global_client_caps & (CAP_NT_SMBS | CAP_STATUS32)) )
612 SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
613 return(ERROR(0,0xc0000000|NT_STATUS_PATH_NOT_COVERED));
615 return(ERROR(ERRSRV,ERRbadpath));
618 /**********************************************************************
619 The following functions are called by the NETDFS RPC pipe functions
620 **********************************************************************/
621 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
627 if(!form_path_from_junction(jn, path, sizeof(path)))
630 /* form the msdfs_link contents */
631 pstrcpy(msdfs_link, "msdfs:");
632 for(i=0; i<jn->referral_count; i++)
634 char* refpath = jn->referral_list[i].alternate_path;
636 trim_string(refpath, "\\", "\\");
641 pstrcat(msdfs_link, ",");
643 pstrcat(msdfs_link, refpath);
646 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
653 if(symlink(msdfs_link, path) < 0)
655 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
656 path, msdfs_link, strerror(errno)));
662 BOOL remove_msdfs_link(struct junction_map* jn)
666 if(!form_path_from_junction(jn, path, sizeof(path)))
675 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
681 char* connect_path = lp_pathname(snum);
682 char* service_name = lp_servicename(snum);
684 if(*connect_path == '\0')
687 /* form a junction for the msdfs root - convention */
689 pstrpcy(jn[cnt].service_name, service_name);
690 jn[cnt].volume_name[0] = '\0';
691 jn[cnt].referral_count = 1;
693 slprintf(alt_path,"\\\\%s\\%s", global_myname, service_name);
697 dirp = opendir(connect_path);
701 while((dname = readdirname(dirp)) != NULL)
707 pstrcpy(pathreal, connect_path);
708 pstrcat(pathreal, "/");
709 pstrcat(pathreal, dname);
711 if(lstat(pathreal,&st) != 0)
713 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
716 if(S_ISLNK(st.st_mode))
718 buflen = readlink(pathreal, buf, sizeof(fstring));
720 if(parse_symlink(buf, &(jn[cnt].referral_list),
721 &(jn[cnt].referral_count)))
723 pstrcpy(jn[cnt].service_name, service_name);
724 pstrcpy(jn[cnt].volume_name, dname);
735 int enum_msdfs_links(struct junction_map* jn)
743 for(i=0;*lp_servicename(i);i++)
746 form_junctions(i,jn,&jn_count);
753 /* Stub functions if WITH_MSDFS not defined */
754 int setup_dfs_referral(char* pathname, int max_referral_level,
760 BOOL is_msdfs_link(connection_struct* conn, char* path)