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 */
265 readlink(path
, buf
, sizeof(buf
));
266 DEBUG(5,("get_referred_path: Referral: %s\n",buf
));
267 if(parse_symlink(buf
, &junction
->referral_list
,
268 &junction
->referral_count
))
274 /**************************************************************
275 Decides if given pathname is Dfs and if it should be redirected
276 Converts pathname to non-dfs format if Dfs redirection not required
277 **************************************************************/
278 BOOL
dfs_redirect(char* pathname
, connection_struct
* conn
)
284 pstrcpy(temp
,pathname
);
286 if(!lp_msdfs_root(SNUM(conn
)) )
289 parse_dfs_path(pathname
,&dp
);
291 if(global_myname
&& (strcasecmp(global_myname
,dp
.hostname
)!=0))
294 /* check if need to redirect */
295 fstrcpy(path
, conn
->connectpath
);
297 fstrcat(path
, dp
.volumename
);
298 if(is_msdfs_link(conn
, path
))
300 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp
));
305 create_nondfs_path(pathname
,&dp
);
306 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
313 Special DFS redirect call for findfirst's.
314 If the findfirst is for the dfs junction, then no redirection,
315 if it is for the underlying directory contents, redirect.
317 BOOL
dfs_findfirst_redirect(char* pathname
, connection_struct
* conn
)
323 pstrcpy(temp
,pathname
);
325 /* Is the path Dfs-redirectable? */
326 if(!dfs_redirect(temp
,conn
))
328 pstrcpy(pathname
,temp
);
332 parse_dfs_path(pathname
,&dp
);
333 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",pathname
,dp
.restofthepath
));
334 if(*(dp
.restofthepath
))
338 create_nondfs_path(pathname
,&dp
);
343 static int setup_ver2_dfs_referral(char* pathname
, char** ppdata
,
344 struct junction_map
* junction
,
347 char* pdata
= *ppdata
;
349 unsigned char uni_requestedpath
[1024];
350 int uni_reqpathoffset1
,uni_reqpathoffset2
;
352 int requestedpathlen
=0;
357 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
359 requestedpathlen
= (dos_struni2(uni_requestedpath
,pathname
,512)+1)*2;
361 dump_data(10,uni_requestedpath
,requestedpathlen
);
363 DEBUG(10,("ref count = %u\n",junction
->referral_count
));
365 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+
366 VERSION2_REFERRAL_SIZE
* junction
->referral_count
;
368 uni_reqpathoffset2
= uni_reqpathoffset1
+ requestedpathlen
;
370 uni_curroffset
= uni_reqpathoffset2
+ requestedpathlen
;
372 reply_size
= REFERRAL_HEADER_SIZE
+ VERSION2_REFERRAL_SIZE
*junction
->referral_count
+
373 2 * requestedpathlen
;
374 DEBUG(10,("reply_size: %u\n",reply_size
));
376 /* add up the unicode lengths of all the referral paths */
377 for(i
=0;i
<junction
->referral_count
;i
++)
379 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
380 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
383 DEBUG(10,("reply_size = %u\n",reply_size
));
384 /* add the unexplained 0x16 bytes */
387 pdata
= *ppdata
= Realloc(pdata
,reply_size
);
390 DEBUG(0,("malloc failed for Realloc!\n"));
394 /* copy in the dfs requested paths.. required for offset calculations */
395 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
396 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
399 /* create the header */
400 SSVAL(pdata
,0,requestedpathlen
-2); /* path consumed */
401 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
403 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
405 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
408 /* add the referral elements */
409 for(i
=0;i
<junction
->referral_count
;i
++)
411 struct referral
* ref
= &(junction
->referral_list
[i
]);
414 SSVAL(pdata
,offset
,2); /* version 2 */
415 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
417 SSVAL(pdata
,offset
+4,1);
419 SSVAL(pdata
,offset
+4,0);
420 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
421 SIVAL(pdata
,offset
+8,ref
->proximity
);
422 SIVAL(pdata
,offset
+12,ref
->ttl
);
424 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
425 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
426 /* copy referred path into current offset */
427 unilen
= (dos_struni2(pdata
+uni_curroffset
,ref
->alternate_path
,512)
429 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
431 uni_curroffset
+= unilen
;
432 offset
+= VERSION2_REFERRAL_SIZE
;
434 /* add in the unexplained 22 (0x16) bytes at the end */
435 memset(pdata
+uni_curroffset
,'\0',0x16);
436 free(junction
->referral_list
);
440 static int setup_ver3_dfs_referral(char* pathname
, char** ppdata
,
441 struct junction_map
* junction
,
444 char* pdata
= *ppdata
;
446 unsigned char uni_reqpath
[1024];
447 int uni_reqpathoffset1
, uni_reqpathoffset2
;
454 DEBUG(10,("setting up version3 referral\n"));
456 reqpathlen
= (dos_struni2(uni_reqpath
,pathname
,512)+1)*2;
458 dump_data(10,uni_reqpath
,reqpathlen
);
460 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+ VERSION3_REFERRAL_SIZE
*
461 junction
->referral_count
;
462 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
463 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
465 for(i
=0;i
<junction
->referral_count
;i
++)
467 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
468 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
471 pdata
= *ppdata
= Realloc(pdata
,reply_size
);
474 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
478 /* create the header */
479 SSVAL(pdata
,0,reqpathlen
-2); /* path consumed */
480 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
482 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
484 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
486 /* copy in the reqpaths */
487 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
488 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
491 for(i
=0;i
<junction
->referral_count
;i
++)
493 struct referral
* ref
= &(junction
->referral_list
[i
]);
496 SSVAL(pdata
,offset
,3); /* version 3 */
497 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
499 SSVAL(pdata
,offset
+4,1);
501 SSVAL(pdata
,offset
+4,0);
503 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
504 SIVAL(pdata
,offset
+8,ref
->ttl
);
506 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
507 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
508 /* copy referred path into current offset */
509 unilen
= (dos_struni2(pdata
+uni_curroffset
,ref
->alternate_path
,512)
511 SSVAL(pdata
,offset
+16,uni_curroffset
-offset
);
512 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
513 memset(pdata
+offset
+18,'\0',16);
515 uni_curroffset
+= unilen
;
516 offset
+= VERSION3_REFERRAL_SIZE
;
518 free(junction
->referral_list
);
524 /******************************************************************
525 * Set up the Dfs referral for the dfs pathname
526 ******************************************************************/
527 int setup_dfs_referral(char* pathname
, int max_referral_level
,
530 struct junction_map junction
;
536 ZERO_STRUCT(junction
);
538 if(!create_junction(pathname
, &junction
))
541 /* get the junction entry */
542 if(!get_referred_path(&junction
))
545 /* refer the same pathname, create a standard referral struct */
546 struct referral
* ref
;
547 self_referral
= True
;
548 junction
.referral_count
= 1;
549 if((ref
= (struct referral
*) malloc(sizeof(struct referral
))) == NULL
)
551 DEBUG(0,("malloc failed for referral\n"));
555 pstrcpy(ref
->alternate_path
,pathname
);
557 ref
->ttl
= REFERRAL_TTL
;
558 junction
.referral_list
= ref
;
562 self_referral
= False
;
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
);
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) max_referral_level
= 2;
577 switch(max_referral_level
)
581 reply_size
= setup_ver2_dfs_referral(pathname
, ppdata
, &junction
,
587 reply_size
= setup_ver3_dfs_referral(pathname
, ppdata
, &junction
,
593 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n",
594 max_referral_level
));
599 DEBUG(10,("DFS Referral pdata:\n"));
600 dump_data(10,*ppdata
,reply_size
);
604 int dfs_path_error(char* inbuf
, char* outbuf
)
606 enum remote_arch_types ra_type
= get_remote_arch();
607 BOOL NT_arch
= ((ra_type
==RA_WINNT
) || (ra_type
== RA_WIN2K
));
608 if(NT_arch
&& (global_client_caps
& (CAP_NT_SMBS
| CAP_STATUS32
)) )
610 SSVAL(outbuf
,smb_flg2
,SVAL(outbuf
,smb_flg2
) | FLAGS2_32_BIT_ERROR_CODES
);
611 return(ERROR(0,0xc0000000|NT_STATUS_PATH_NOT_COVERED
));
613 return(ERROR(ERRSRV
,ERRbadpath
));
616 /**********************************************************************
617 The following functions are called by the NETDFS RPC pipe functions
618 **********************************************************************/
619 BOOL
create_msdfs_link(struct junction_map
* jn
, BOOL exists
)
625 if(!form_path_from_junction(jn
, path
, sizeof(path
)))
628 /* form the msdfs_link contents */
629 pstrcpy(msdfs_link
, "msdfs:");
630 for(i
=0; i
<jn
->referral_count
; i
++)
632 char* refpath
= jn
->referral_list
[i
].alternate_path
;
634 trim_string(refpath
, "\\", "\\");
639 pstrcat(msdfs_link
, ",");
641 pstrcat(msdfs_link
, refpath
);
644 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
651 if(symlink(msdfs_link
, path
) < 0)
653 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
654 path
, msdfs_link
, strerror(errno
)));
660 BOOL
remove_msdfs_link(struct junction_map
* jn
)
664 if(!form_path_from_junction(jn
, path
, sizeof(path
)))
673 static BOOL
form_junctions(int snum
, struct junction_map
* jn
, int* jn_count
)
679 char* connect_path
= lp_pathname(snum
);
680 char* service_name
= lp_servicename(snum
);
682 if(*connect_path
== '\0')
685 /* form a junction for the msdfs root - convention */
687 pstrpcy(jn[cnt].service_name, service_name);
688 jn[cnt].volume_name[0] = '\0';
689 jn[cnt].referral_count = 1;
691 slprintf(alt_path,"\\\\%s\\%s", global_myname, service_name);
695 dirp
= opendir(connect_path
);
699 while((dname
= readdirname(dirp
)) != NULL
)
705 pstrcpy(pathreal
, connect_path
);
706 pstrcat(pathreal
, "/");
707 pstrcat(pathreal
, dname
);
709 if(lstat(pathreal
,&st
) != 0)
711 DEBUG(4,("lstat error for %s: %s\n",pathreal
, strerror(errno
)));
714 if(S_ISLNK(st
.st_mode
))
716 buflen
= readlink(pathreal
, buf
, sizeof(fstring
));
718 if(parse_symlink(buf
, &(jn
[cnt
].referral_list
),
719 &(jn
[cnt
].referral_count
)))
721 pstrcpy(jn
[cnt
].service_name
, service_name
);
722 pstrcpy(jn
[cnt
].volume_name
, dname
);
733 int enum_msdfs_links(struct junction_map
* jn
)
741 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
,
758 BOOL
is_msdfs_link(connection_struct
* conn
, char* path
)