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 **********************************************************************/
34 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
36 pstrcpy(pathname,pdp->volumename);
37 pstrcat(pathname,"\\");
38 pstrcat(pathname,pdp->restofthepath);
41 /**********************************************************************
42 Parse the pathname of the form \hostname\service\volume\restofthepath
43 into the dfs_path structure
44 **********************************************************************/
46 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
48 pstring pathname_local;
51 pstrcpy(pathname_local,pathname);
52 p = temp = pathname_local;
56 trim_string(temp,"\\","\\");
57 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
60 /* parse out hostname */
61 p = strchr_m(temp,'\\');
65 pstrcpy(pdp->hostname,temp);
66 DEBUG(10,("hostname: %s\n",pdp->hostname));
68 /* parse out servicename */
70 p = strchr_m(temp,'\\');
72 pstrcpy(pdp->servicename,temp);
76 pstrcpy(pdp->servicename,temp);
77 DEBUG(10,("servicename: %s\n",pdp->servicename));
79 /* parse out volumename */
81 p = strchr_m(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 Fake up a connection struct for the VFS layer.
98 *********************************************************/
100 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
103 conn->service = snum;
104 conn->connectpath = path;
106 if (!vfs_init(conn)) {
107 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
113 /**********************************************************************
114 Forms a valid Unix pathname from the junction
115 **********************************************************************/
117 static BOOL form_path_from_junction(struct junction_map* jn, char* path, int max_pathlen,
118 connection_struct *conn)
125 snum = lp_servicenumber(jn->service_name);
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);
134 if (!create_conn_struct(conn, snum, path))
140 /**********************************************************************
141 Creates a junction structure from the Dfs pathname
142 **********************************************************************/
144 BOOL create_junction(char* pathname, struct junction_map* jn)
148 parse_dfs_path(pathname,&dp);
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));
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));
162 pstrcpy(jn->service_name,dp.servicename);
163 pstrcpy(jn->volume_name,dp.volumename);
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 **********************************************************************/
172 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
176 char* alt_path[MAX_REFERRAL_COUNT];
178 struct referral* reflist;
182 prot = strtok(temp,":");
184 if(!strequal(prot, "msdfs"))
187 /* It's an msdfs referral */
191 /* parse out the alternate paths */
192 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
195 DEBUG(10,("parse_symlink: count=%d\n", count));
197 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
198 if(reflist == NULL) {
199 DEBUG(0,("parse_symlink: Malloc failed!\n"));
203 for(i=0;i<count;i++) {
204 /* replace / in the alternate path by a \ */
205 char* p = strchr_m(alt_path[i],'/');
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));
222 /**********************************************************************
223 Returns true if the unix path is a valid msdfs symlink
224 **********************************************************************/
226 BOOL is_msdfs_link(connection_struct* conn, char* path)
230 int referral_len = 0;
237 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
238 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
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)));
248 referral[referral_len] = '\0';
249 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
250 if(parse_symlink(referral, NULL, NULL))
256 /**********************************************************************
257 Fills in the junction_map struct with the referrals from the
259 **********************************************************************/
261 BOOL get_referred_path(struct junction_map* junction)
266 connection_struct conns;
267 connection_struct *conn = &conns;
269 if(!form_path_from_junction(junction, path, sizeof(path), conn))
272 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
274 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
275 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
279 if(S_ISLNK(st.st_mode)) {
280 /* open the link and read it to get the dfs referral */
282 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
284 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
285 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
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 **************************************************************/
296 BOOL dfs_redirect(char* pathname, connection_struct* conn)
302 pstrcpy(temp,pathname);
304 if(!lp_msdfs_root(SNUM(conn)) )
307 parse_dfs_path(pathname,&dp);
309 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
312 /* check if need to redirect */
313 fstrcpy(path, conn->connectpath);
315 fstrcat(path, dp.volumename);
316 if(is_msdfs_link(conn, path)) {
317 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
320 create_nondfs_path(pathname,&dp);
321 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
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.
333 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
339 pstrcpy(temp,pathname);
341 /* Is the path Dfs-redirectable? */
342 if(!dfs_redirect(temp,conn)) {
343 pstrcpy(pathname,temp);
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);
358 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
359 struct junction_map* junction,
362 char* pdata = *ppdata;
364 unsigned char uni_requestedpath[1024];
365 int uni_reqpathoffset1,uni_reqpathoffset2;
367 int requestedpathlen=0;
372 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
374 requestedpathlen = (dos_struni2(uni_requestedpath,pathname,512)+1)*2;
376 dump_data(10,uni_requestedpath,requestedpathlen);
378 DEBUG(10,("ref count = %u\n",junction->referral_count));
380 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
381 VERSION2_REFERRAL_SIZE * junction->referral_count;
383 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
385 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
387 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
388 2 * requestedpathlen;
389 DEBUG(10,("reply_size: %u\n",reply_size));
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;
397 DEBUG(10,("reply_size = %u\n",reply_size));
398 /* add the unexplained 0x16 bytes */
401 pdata = *ppdata = Realloc(pdata,reply_size);
403 DEBUG(0,("malloc failed for Realloc!\n"));
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);
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 */
415 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
417 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
420 /* add the referral elements */
421 for(i=0;i<junction->referral_count;i++) {
422 struct referral* ref = &(junction->referral_list[i]);
425 SSVAL(pdata,offset,2); /* version 2 */
426 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
428 SSVAL(pdata,offset+4,1);
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);
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);
441 uni_curroffset += unilen;
442 offset += VERSION2_REFERRAL_SIZE;
444 /* add in the unexplained 22 (0x16) bytes at the end */
445 memset(pdata+uni_curroffset,'\0',0x16);
446 free(junction->referral_list);
450 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
451 struct junction_map* junction,
454 char* pdata = *ppdata;
456 unsigned char uni_reqpath[1024];
457 int uni_reqpathoffset1, uni_reqpathoffset2;
464 DEBUG(10,("setting up version3 referral\n"));
466 reqpathlen = (dos_struni2(uni_reqpath,pathname,512)+1)*2;
468 dump_data(10,uni_reqpath,reqpathlen);
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;
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;
479 pdata = *ppdata = Realloc(pdata,reply_size);
481 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
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 */
489 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
491 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
493 /* copy in the reqpaths */
494 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
495 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
498 for(i=0;i<junction->referral_count;i++) {
499 struct referral* ref = &(junction->referral_list[i]);
502 SSVAL(pdata,offset,3); /* version 3 */
503 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
505 SSVAL(pdata,offset+4,1);
507 SSVAL(pdata,offset+4,0);
509 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
510 SIVAL(pdata,offset+8,ref->ttl);
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);
520 uni_curroffset += unilen;
521 offset += VERSION3_REFERRAL_SIZE;
523 free(junction->referral_list);
527 /******************************************************************
528 * Set up the Dfs referral for the dfs pathname
529 ******************************************************************/
531 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
533 struct junction_map junction;
539 ZERO_STRUCT(junction);
541 if(!create_junction(pathname, &junction))
544 /* get the junction entry */
545 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) {
552 DEBUG(0,("malloc failed for referral\n"));
556 pstrcpy(ref->alternate_path,pathname);
558 ref->ttl = REFERRAL_TTL;
559 junction.referral_list = ref;
561 self_referral = False;
562 if( DEBUGLVL( 3 ) ) {
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);
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;
576 switch(max_referral_level) {
579 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction, self_referral);
584 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction, self_referral);
589 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
594 DEBUG(10,("DFS Referral pdata:\n"));
595 dump_data(10,*ppdata,reply_size);
599 int dfs_path_error(char* inbuf, char* outbuf)
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));
607 return(ERROR(ERRSRV,ERRbadpath));
610 /**********************************************************************
611 The following functions are called by the NETDFS RPC pipe functions
612 **********************************************************************/
614 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
618 connection_struct conns;
619 connection_struct *conn = &conns;
622 if(!form_path_from_junction(jn, path, sizeof(path), conn))
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;
630 trim_string(refpath, "\\", "\\");
635 pstrcat(msdfs_link, ",");
637 pstrcat(msdfs_link, refpath);
640 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
643 if(conn->vfs_ops.unlink(conn,path)!=0)
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)));
654 BOOL remove_msdfs_link(struct junction_map* jn)
657 connection_struct conns;
658 connection_struct *conn = &conns;
660 if(!form_path_from_junction(jn, path, sizeof(path), conn))
663 if(conn->vfs_ops.unlink(conn, path)!=0)
669 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
674 pstring connect_path;
675 char* service_name = lp_servicename(snum);
676 connection_struct conns;
677 connection_struct *conn = &conns;
679 pstrcpy(connect_path,lp_pathname(snum));
681 if(*connect_path == '\0')
685 * Fake up a connection struct for the VFS layer.
688 if (!create_conn_struct(conn, snum, connect_path))
691 /* form a junction for the msdfs root - convention */
693 pstrpcy(jn[cnt].service_name, service_name);
694 jn[cnt].volume_name[0] = '\0';
695 jn[cnt].referral_count = 1;
697 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
701 dirp = conn->vfs_ops.opendir(conn, connect_path);
705 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
710 pstrcpy(pathreal, connect_path);
711 pstrcat(pathreal, "/");
712 pstrcat(pathreal, dname);
714 if(conn->vfs_ops.lstat(conn,pathreal,&st) != 0) {
715 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
718 if(S_ISLNK(st.st_mode)) {
719 buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
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);
729 conn->vfs_ops.closedir(conn,dirp);
734 int enum_msdfs_links(struct junction_map* jn)
742 for(i=0;*lp_servicename(i);i++) {
744 form_junctions(i,jn,&jn_count);
751 /* Stub functions if WITH_MSDFS not defined */
752 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
757 BOOL is_msdfs_link(connection_struct* conn, char* path)