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
= Realloc(pdata
,reply_size
);
403 DEBUG(0,("malloc failed for Realloc!\n"));
406 else *ppdata
= pdata
;
408 /* copy in the dfs requested paths.. required for offset calculations */
409 memcpy(pdata
+uni_reqpathoffset1
,uni_requestedpath
,requestedpathlen
);
410 memcpy(pdata
+uni_reqpathoffset2
,uni_requestedpath
,requestedpathlen
);
412 /* create the header */
413 SSVAL(pdata
,0,requestedpathlen
-2); /* path consumed */
414 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
416 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
418 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
421 /* add the referral elements */
422 for(i
=0;i
<junction
->referral_count
;i
++) {
423 struct referral
* ref
= &(junction
->referral_list
[i
]);
426 SSVAL(pdata
,offset
,2); /* version 2 */
427 SSVAL(pdata
,offset
+2,VERSION2_REFERRAL_SIZE
);
429 SSVAL(pdata
,offset
+4,1);
431 SSVAL(pdata
,offset
+4,0);
432 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
433 SIVAL(pdata
,offset
+8,ref
->proximity
);
434 SIVAL(pdata
,offset
+12,ref
->ttl
);
436 SSVAL(pdata
,offset
+16,uni_reqpathoffset1
-offset
);
437 SSVAL(pdata
,offset
+18,uni_reqpathoffset2
-offset
);
438 /* copy referred path into current offset */
439 unilen
= (dos_struni2(pdata
+uni_curroffset
,ref
->alternate_path
,512) +1)*2;
440 SSVAL(pdata
,offset
+20,uni_curroffset
-offset
);
442 uni_curroffset
+= unilen
;
443 offset
+= VERSION2_REFERRAL_SIZE
;
445 /* add in the unexplained 22 (0x16) bytes at the end */
446 memset(pdata
+uni_curroffset
,'\0',0x16);
447 free(junction
->referral_list
);
451 static int setup_ver3_dfs_referral(char* pathname
, char** ppdata
,
452 struct junction_map
* junction
,
455 char* pdata
= *ppdata
;
457 unsigned char uni_reqpath
[1024];
458 int uni_reqpathoffset1
, uni_reqpathoffset2
;
465 DEBUG(10,("setting up version3 referral\n"));
467 reqpathlen
= (dos_struni2(uni_reqpath
,pathname
,512)+1)*2;
469 dump_data(10,uni_reqpath
,reqpathlen
);
471 uni_reqpathoffset1
= REFERRAL_HEADER_SIZE
+ VERSION3_REFERRAL_SIZE
* junction
->referral_count
;
472 uni_reqpathoffset2
= uni_reqpathoffset1
+ reqpathlen
;
473 reply_size
= uni_curroffset
= uni_reqpathoffset2
+ reqpathlen
;
475 for(i
=0;i
<junction
->referral_count
;i
++) {
476 DEBUG(10,("referral %u : %s\n",i
,junction
->referral_list
[i
].alternate_path
));
477 reply_size
+= (strlen(junction
->referral_list
[i
].alternate_path
)+1)*2;
480 pdata
= Realloc(pdata
,reply_size
);
482 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
485 else *ppdata
= pdata
;
487 /* create the header */
488 SSVAL(pdata
,0,reqpathlen
-2); /* path consumed */
489 SSVAL(pdata
,2,junction
->referral_count
); /* number of referral in this pkt */
491 SIVAL(pdata
,4,DFSREF_REFERRAL_SERVER
| DFSREF_STORAGE_SERVER
);
493 SIVAL(pdata
,4,DFSREF_STORAGE_SERVER
);
495 /* copy in the reqpaths */
496 memcpy(pdata
+uni_reqpathoffset1
,uni_reqpath
,reqpathlen
);
497 memcpy(pdata
+uni_reqpathoffset2
,uni_reqpath
,reqpathlen
);
500 for(i
=0;i
<junction
->referral_count
;i
++) {
501 struct referral
* ref
= &(junction
->referral_list
[i
]);
504 SSVAL(pdata
,offset
,3); /* version 3 */
505 SSVAL(pdata
,offset
+2,VERSION3_REFERRAL_SIZE
);
507 SSVAL(pdata
,offset
+4,1);
509 SSVAL(pdata
,offset
+4,0);
511 SSVAL(pdata
,offset
+6,0); /* ref_flags :use path_consumed bytes? */
512 SIVAL(pdata
,offset
+8,ref
->ttl
);
514 SSVAL(pdata
,offset
+12,uni_reqpathoffset1
-offset
);
515 SSVAL(pdata
,offset
+14,uni_reqpathoffset2
-offset
);
516 /* copy referred path into current offset */
517 unilen
= (dos_struni2(pdata
+uni_curroffset
,ref
->alternate_path
,512) +1)*2;
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);
522 uni_curroffset
+= unilen
;
523 offset
+= VERSION3_REFERRAL_SIZE
;
525 free(junction
->referral_list
);
529 /******************************************************************
530 * Set up the Dfs referral for the dfs pathname
531 ******************************************************************/
533 int setup_dfs_referral(char* pathname
, int max_referral_level
, char** ppdata
)
535 struct junction_map junction
;
541 ZERO_STRUCT(junction
);
543 if(!create_junction(pathname
, &junction
))
546 /* get the junction entry */
547 if(!get_referred_path(&junction
)) {
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"));
558 pstrcpy(ref
->alternate_path
,pathname
);
560 ref
->ttl
= REFERRAL_TTL
;
561 junction
.referral_list
= ref
;
563 self_referral
= False
;
564 if( DEBUGLVL( 3 ) ) {
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)
576 max_referral_level
= 2;
578 switch(max_referral_level
) {
581 reply_size
= setup_ver2_dfs_referral(pathname
, ppdata
, &junction
, self_referral
);
586 reply_size
= setup_ver3_dfs_referral(pathname
, ppdata
, &junction
, self_referral
);
591 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level
));
596 DEBUG(10,("DFS Referral pdata:\n"));
597 dump_data(10,*ppdata
,reply_size
);
601 int dfs_path_error(char* inbuf
, char* outbuf
)
603 enum remote_arch_types ra_type
= get_remote_arch();
604 BOOL NT_arch
= ((ra_type
==RA_WINNT
) || (ra_type
== RA_WIN2K
));
605 if(NT_arch
&& (global_client_caps
& (CAP_NT_SMBS
| CAP_STATUS32
)) ) {
606 SSVAL(outbuf
,smb_flg2
,SVAL(outbuf
,smb_flg2
) | FLAGS2_32_BIT_ERROR_CODES
);
607 return(ERROR(0,0xc0000000|NT_STATUS_PATH_NOT_COVERED
));
609 return(ERROR(ERRSRV
,ERRbadpath
));
612 /**********************************************************************
613 The following functions are called by the NETDFS RPC pipe functions
614 **********************************************************************/
616 BOOL
create_msdfs_link(struct junction_map
* jn
, BOOL exists
)
620 connection_struct conns
;
621 connection_struct
*conn
= &conns
;
624 if(!form_path_from_junction(jn
, path
, sizeof(path
), conn
))
627 /* form the msdfs_link contents */
628 pstrcpy(msdfs_link
, "msdfs:");
629 for(i
=0; i
<jn
->referral_count
; i
++) {
630 char* refpath
= jn
->referral_list
[i
].alternate_path
;
632 trim_string(refpath
, "\\", "\\");
637 pstrcat(msdfs_link
, ",");
639 pstrcat(msdfs_link
, refpath
);
642 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path
, msdfs_link
));
645 if(conn
->vfs_ops
.unlink(conn
,path
)!=0)
648 if(conn
->vfs_ops
.symlink(conn
, msdfs_link
, path
) < 0) {
649 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
650 path
, msdfs_link
, strerror(errno
)));
656 BOOL
remove_msdfs_link(struct junction_map
* jn
)
659 connection_struct conns
;
660 connection_struct
*conn
= &conns
;
662 if(!form_path_from_junction(jn
, path
, sizeof(path
), conn
))
665 if(conn
->vfs_ops
.unlink(conn
, path
)!=0)
671 static BOOL
form_junctions(int snum
, struct junction_map
* jn
, int* jn_count
)
676 pstring connect_path
;
677 char* service_name
= lp_servicename(snum
);
678 connection_struct conns
;
679 connection_struct
*conn
= &conns
;
681 pstrcpy(connect_path
,lp_pathname(snum
));
683 if(*connect_path
== '\0')
687 * Fake up a connection struct for the VFS layer.
690 if (!create_conn_struct(conn
, snum
, connect_path
))
693 /* form a junction for the msdfs root - convention */
695 pstrpcy(jn[cnt].service_name, service_name);
696 jn[cnt].volume_name[0] = '\0';
697 jn[cnt].referral_count = 1;
699 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
703 dirp
= conn
->vfs_ops
.opendir(conn
, connect_path
);
707 while((dname
= vfs_readdirname(conn
, dirp
)) != NULL
) {
712 pstrcpy(pathreal
, connect_path
);
713 pstrcat(pathreal
, "/");
714 pstrcat(pathreal
, dname
);
716 if(conn
->vfs_ops
.lstat(conn
,pathreal
,&st
) != 0) {
717 DEBUG(4,("lstat error for %s: %s\n",pathreal
, strerror(errno
)));
720 if(S_ISLNK(st
.st_mode
)) {
721 buflen
= conn
->vfs_ops
.readlink(conn
, pathreal
, buf
, sizeof(fstring
));
723 if(parse_symlink(buf
, &(jn
[cnt
].referral_list
), &(jn
[cnt
].referral_count
))) {
724 pstrcpy(jn
[cnt
].service_name
, service_name
);
725 pstrcpy(jn
[cnt
].volume_name
, dname
);
731 conn
->vfs_ops
.closedir(conn
,dirp
);
736 int enum_msdfs_links(struct junction_map
* jn
)
744 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
, char** ppdata
)
759 BOOL
is_msdfs_link(connection_struct
* conn
, char* path
)