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 pstring global_myname;
26 /**********************************************************************
27 Create a tcon relative path from a dfs_path structure
28 **********************************************************************/
30 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
32 pstrcpy(pathname,pdp->volumename);
33 pstrcat(pathname,"\\");
34 pstrcat(pathname,pdp->restofthepath);
37 /**********************************************************************
38 Parse the pathname of the form \hostname\service\volume\restofthepath
39 into the dfs_path structure
40 **********************************************************************/
42 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
44 pstring pathname_local;
47 pstrcpy(pathname_local,pathname);
48 p = temp = pathname_local;
52 trim_string(temp,"\\","\\");
53 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
56 /* parse out hostname */
57 p = strchr_m(temp,'\\');
61 pstrcpy(pdp->hostname,temp);
62 DEBUG(10,("hostname: %s\n",pdp->hostname));
64 /* parse out servicename */
66 p = strchr_m(temp,'\\');
68 pstrcpy(pdp->servicename,temp);
72 pstrcpy(pdp->servicename,temp);
73 DEBUG(10,("servicename: %s\n",pdp->servicename));
75 /* parse out volumename */
77 p = strchr_m(temp,'\\');
79 pstrcpy(pdp->volumename,temp);
83 pstrcpy(pdp->volumename,temp);
84 DEBUG(10,("volumename: %s\n",pdp->volumename));
86 /* remaining path .. */
87 pstrcpy(pdp->restofthepath,p+1);
88 DEBUG(10,("rest of the path: %s\n",pdp->restofthepath));
92 /********************************************************
93 Fake up a connection struct for the VFS layer.
94 *********************************************************/
96 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
100 conn->connectpath = path;
102 if (!vfs_init(conn)) {
103 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
109 /**********************************************************************
110 Forms a valid Unix pathname from the junction
111 **********************************************************************/
113 static BOOL form_path_from_junction(struct junction_map* jn, char* path, int max_pathlen,
114 connection_struct *conn)
121 snum = lp_servicenumber(jn->service_name);
125 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
126 safe_strcat(path, "/", max_pathlen-1);
127 strlower(jn->volume_name);
128 safe_strcat(path, jn->volume_name, max_pathlen-1);
130 if (!create_conn_struct(conn, snum, path))
136 /**********************************************************************
137 Creates a junction structure from the Dfs pathname
138 **********************************************************************/
140 BOOL create_junction(char* pathname, struct junction_map* jn)
144 parse_dfs_path(pathname,&dp);
146 /* check if path is dfs : check hostname is the first token */
147 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0)) {
148 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n", dp.hostname, pathname));
152 /* Check for a non-DFS share */
153 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
154 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
158 pstrcpy(jn->service_name,dp.servicename);
159 pstrcpy(jn->volume_name,dp.volumename);
163 /**********************************************************************
164 Parse the contents of a symlink to verify if it is an msdfs referral
165 A valid referral is of the form: msdfs:server1\share1,server2\share2
166 **********************************************************************/
168 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
172 char* alt_path[MAX_REFERRAL_COUNT];
174 struct referral* reflist;
178 prot = strtok(temp,":");
180 if(!strequal(prot, "msdfs"))
183 /* It's an msdfs referral */
187 /* parse out the alternate paths */
188 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
191 DEBUG(10,("parse_symlink: count=%d\n", count));
193 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
194 if(reflist == NULL) {
195 DEBUG(0,("parse_symlink: Malloc failed!\n"));
199 for(i=0;i<count;i++) {
200 /* replace / in the alternate path by a \ */
201 char* p = strchr_m(alt_path[i],'/');
205 pstrcpy(reflist[i].alternate_path, "\\");
206 pstrcat(reflist[i].alternate_path, alt_path[i]);
207 reflist[i].proximity = 0;
208 reflist[i].ttl = REFERRAL_TTL;
209 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
218 /**********************************************************************
219 Returns true if the unix path is a valid msdfs symlink
220 **********************************************************************/
222 BOOL is_msdfs_link(connection_struct* conn, char* path)
226 int referral_len = 0;
233 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
234 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
238 if(S_ISLNK(st.st_mode)) {
239 /* open the link and read it */
240 referral_len = conn->vfs_ops.readlink(conn, path, referral, sizeof(pstring));
241 if(referral_len == -1)
242 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
244 referral[referral_len] = '\0';
245 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
246 if(parse_symlink(referral, NULL, NULL))
252 /**********************************************************************
253 Fills in the junction_map struct with the referrals from the
255 **********************************************************************/
257 BOOL get_referred_path(struct junction_map* junction)
262 connection_struct conns;
263 connection_struct *conn = &conns;
265 if(!form_path_from_junction(junction, path, sizeof(path), conn))
268 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
270 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
271 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
275 if(S_ISLNK(st.st_mode)) {
276 /* open the link and read it to get the dfs referral */
278 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
280 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
281 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
287 /**************************************************************
288 Decides if given pathname is Dfs and if it should be redirected
289 Converts pathname to non-dfs format if Dfs redirection not required
290 **************************************************************/
292 BOOL dfs_redirect(char* pathname, connection_struct* conn)
298 pstrcpy(temp,pathname);
300 if(!lp_msdfs_root(SNUM(conn)) )
303 parse_dfs_path(pathname,&dp);
305 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
308 /* check if need to redirect */
309 fstrcpy(path, conn->connectpath);
311 fstrcat(path, dp.volumename);
312 if(is_msdfs_link(conn, path)) {
313 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
316 create_nondfs_path(pathname,&dp);
317 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
324 Special DFS redirect call for findfirst's.
325 If the findfirst is for the dfs junction, then no redirection,
326 if it is for the underlying directory contents, redirect.
329 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
335 pstrcpy(temp,pathname);
337 /* Is the path Dfs-redirectable? */
338 if(!dfs_redirect(temp,conn)) {
339 pstrcpy(pathname,temp);
343 parse_dfs_path(pathname,&dp);
344 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",
345 pathname,dp.restofthepath));
346 if(!(*(dp.restofthepath))) {
347 create_nondfs_path(pathname,&dp);
354 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
355 struct junction_map* junction,
358 char* pdata = *ppdata;
360 unsigned char uni_requestedpath[1024];
361 int uni_reqpathoffset1,uni_reqpathoffset2;
363 int requestedpathlen=0;
368 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
370 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
373 dump_data(10,uni_requestedpath,requestedpathlen);
375 DEBUG(10,("ref count = %u\n",junction->referral_count));
377 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
378 VERSION2_REFERRAL_SIZE * junction->referral_count;
380 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
382 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
384 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
385 2 * requestedpathlen;
386 DEBUG(10,("reply_size: %u\n",reply_size));
388 /* add up the unicode lengths of all the referral paths */
389 for(i=0;i<junction->referral_count;i++) {
390 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
391 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
394 DEBUG(10,("reply_size = %u\n",reply_size));
395 /* add the unexplained 0x16 bytes */
398 pdata = Realloc(pdata,reply_size);
400 DEBUG(0,("malloc failed for Realloc!\n"));
403 else *ppdata = pdata;
405 /* copy in the dfs requested paths.. required for offset calculations */
406 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
407 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
409 /* create the header */
410 SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
411 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
413 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
415 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
418 /* add the referral elements */
419 for(i=0;i<junction->referral_count;i++) {
420 struct referral* ref = &(junction->referral_list[i]);
423 SSVAL(pdata,offset,2); /* version 2 */
424 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
426 SSVAL(pdata,offset+4,1);
428 SSVAL(pdata,offset+4,0);
429 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
430 SIVAL(pdata,offset+8,ref->proximity);
431 SIVAL(pdata,offset+12,ref->ttl);
433 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
434 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
435 /* copy referred path into current offset */
436 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
438 SSVAL(pdata,offset+20,uni_curroffset-offset);
440 uni_curroffset += unilen;
441 offset += VERSION2_REFERRAL_SIZE;
443 /* add in the unexplained 22 (0x16) bytes at the end */
444 memset(pdata+uni_curroffset,'\0',0x16);
445 SAFE_FREE(junction->referral_list);
449 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
450 struct junction_map* junction,
453 char* pdata = *ppdata;
455 unsigned char uni_reqpath[1024];
456 int uni_reqpathoffset1, uni_reqpathoffset2;
463 DEBUG(10,("setting up version3 referral\n"));
465 reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
467 dump_data(10,uni_reqpath,reqpathlen);
469 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
470 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
471 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
473 for(i=0;i<junction->referral_count;i++) {
474 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
475 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
478 pdata = Realloc(pdata,reply_size);
480 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
483 else *ppdata = pdata;
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 */
516 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
517 -1, STR_UNICODE|STR_TERMINATE);
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 SAFE_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 /**********************************************************************
602 The following functions are called by the NETDFS RPC pipe functions
603 **********************************************************************/
605 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
609 connection_struct conns;
610 connection_struct *conn = &conns;
613 if(!form_path_from_junction(jn, path, sizeof(path), conn))
616 /* form the msdfs_link contents */
617 pstrcpy(msdfs_link, "msdfs:");
618 for(i=0; i<jn->referral_count; i++) {
619 char* refpath = jn->referral_list[i].alternate_path;
621 trim_string(refpath, "\\", "\\");
626 pstrcat(msdfs_link, ",");
628 pstrcat(msdfs_link, refpath);
631 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
634 if(conn->vfs_ops.unlink(conn,path)!=0)
637 if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
638 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
639 path, msdfs_link, strerror(errno)));
645 BOOL remove_msdfs_link(struct junction_map* jn)
648 connection_struct conns;
649 connection_struct *conn = &conns;
651 if(!form_path_from_junction(jn, path, sizeof(path), conn))
654 if(conn->vfs_ops.unlink(conn, path)!=0)
660 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
665 pstring connect_path;
666 char* service_name = lp_servicename(snum);
667 connection_struct conns;
668 connection_struct *conn = &conns;
670 pstrcpy(connect_path,lp_pathname(snum));
672 if(*connect_path == '\0')
676 * Fake up a connection struct for the VFS layer.
679 if (!create_conn_struct(conn, snum, connect_path))
682 /* form a junction for the msdfs root - convention */
684 pstrpcy(jn[cnt].service_name, service_name);
685 jn[cnt].volume_name[0] = '\0';
686 jn[cnt].referral_count = 1;
688 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
692 dirp = conn->vfs_ops.opendir(conn, connect_path);
696 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
701 pstrcpy(pathreal, connect_path);
702 pstrcat(pathreal, "/");
703 pstrcat(pathreal, dname);
705 if(conn->vfs_ops.lstat(conn,pathreal,&st) != 0) {
706 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
709 if(S_ISLNK(st.st_mode)) {
710 buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
712 if(parse_symlink(buf, &(jn[cnt].referral_list), &(jn[cnt].referral_count))) {
713 pstrcpy(jn[cnt].service_name, service_name);
714 pstrcpy(jn[cnt].volume_name, dname);
720 conn->vfs_ops.closedir(conn,dirp);
725 int enum_msdfs_links(struct junction_map* jn)
733 for(i=0;*lp_servicename(i);i++) {
735 form_junctions(i,jn,&jn_count);