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;
27 /**********************************************************************
28 Create a tcon relative path from a dfs_path structure
29 **********************************************************************/
31 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
33 pstrcpy(pathname,pdp->volumename);
34 pstrcat(pathname,"\\");
35 pstrcat(pathname,pdp->restofthepath);
38 /**********************************************************************
39 Parse the pathname of the form \hostname\service\volume\restofthepath
40 into the dfs_path structure
41 **********************************************************************/
43 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
45 pstring pathname_local;
48 pstrcpy(pathname_local,pathname);
49 p = temp = pathname_local;
53 trim_string(temp,"\\","\\");
54 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
57 /* parse out hostname */
58 p = strchr_m(temp,'\\');
62 pstrcpy(pdp->hostname,temp);
63 DEBUG(10,("hostname: %s\n",pdp->hostname));
65 /* parse out servicename */
67 p = strchr_m(temp,'\\');
69 pstrcpy(pdp->servicename,temp);
73 pstrcpy(pdp->servicename,temp);
74 DEBUG(10,("servicename: %s\n",pdp->servicename));
76 /* parse out volumename */
78 p = strchr_m(temp,'\\');
80 pstrcpy(pdp->volumename,temp);
84 pstrcpy(pdp->volumename,temp);
85 DEBUG(10,("volumename: %s\n",pdp->volumename));
87 /* remaining path .. */
88 pstrcpy(pdp->restofthepath,p+1);
89 DEBUG(10,("rest of the path: %s\n",pdp->restofthepath));
93 /********************************************************
94 Fake up a connection struct for the VFS layer.
95 *********************************************************/
97 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
100 conn->service = snum;
101 conn->connectpath = path;
103 if (!vfs_init(conn)) {
104 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
110 /**********************************************************************
111 Forms a valid Unix pathname from the junction
112 **********************************************************************/
114 static BOOL form_path_from_junction(struct junction_map* jn, char* path, int max_pathlen,
115 connection_struct *conn)
122 snum = lp_servicenumber(jn->service_name);
126 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
127 safe_strcat(path, "/", max_pathlen-1);
128 strlower(jn->volume_name);
129 safe_strcat(path, jn->volume_name, max_pathlen-1);
131 if (!create_conn_struct(conn, snum, path))
137 /**********************************************************************
138 Creates a junction structure from the Dfs pathname
139 **********************************************************************/
141 BOOL create_junction(char* pathname, struct junction_map* jn)
145 parse_dfs_path(pathname,&dp);
147 /* check if path is dfs : check hostname is the first token */
148 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0)) {
149 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n", dp.hostname, pathname));
153 /* Check for a non-DFS share */
154 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
155 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
159 pstrcpy(jn->service_name,dp.servicename);
160 pstrcpy(jn->volume_name,dp.volumename);
164 /**********************************************************************
165 Parse the contents of a symlink to verify if it is an msdfs referral
166 A valid referral is of the form: msdfs:server1\share1,server2\share2
167 **********************************************************************/
169 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
173 char* alt_path[MAX_REFERRAL_COUNT];
175 struct referral* reflist;
179 prot = strtok(temp,":");
181 if(!strequal(prot, "msdfs"))
184 /* It's an msdfs referral */
188 /* parse out the alternate paths */
189 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
192 DEBUG(10,("parse_symlink: count=%d\n", count));
194 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
195 if(reflist == NULL) {
196 DEBUG(0,("parse_symlink: Malloc failed!\n"));
200 for(i=0;i<count;i++) {
201 /* replace / in the alternate path by a \ */
202 char* p = strchr_m(alt_path[i],'/');
206 pstrcpy(reflist[i].alternate_path, "\\");
207 pstrcat(reflist[i].alternate_path, alt_path[i]);
208 reflist[i].proximity = 0;
209 reflist[i].ttl = REFERRAL_TTL;
210 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
219 /**********************************************************************
220 Returns true if the unix path is a valid msdfs symlink
221 **********************************************************************/
223 BOOL is_msdfs_link(connection_struct* conn, char* path)
227 int referral_len = 0;
234 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
235 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
239 if(S_ISLNK(st.st_mode)) {
240 /* open the link and read it */
241 referral_len = conn->vfs_ops.readlink(conn, path, referral, sizeof(pstring));
242 if(referral_len == -1)
243 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
245 referral[referral_len] = '\0';
246 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
247 if(parse_symlink(referral, NULL, NULL))
253 /**********************************************************************
254 Fills in the junction_map struct with the referrals from the
256 **********************************************************************/
258 BOOL get_referred_path(struct junction_map* junction)
263 connection_struct conns;
264 connection_struct *conn = &conns;
266 if(!form_path_from_junction(junction, path, sizeof(path), conn))
269 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
271 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
272 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
276 if(S_ISLNK(st.st_mode)) {
277 /* open the link and read it to get the dfs referral */
279 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
281 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
282 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
288 /**************************************************************
289 Decides if given pathname is Dfs and if it should be redirected
290 Converts pathname to non-dfs format if Dfs redirection not required
291 **************************************************************/
293 BOOL dfs_redirect(char* pathname, connection_struct* conn)
299 pstrcpy(temp,pathname);
301 if(!lp_msdfs_root(SNUM(conn)) )
304 parse_dfs_path(pathname,&dp);
306 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
309 /* check if need to redirect */
310 fstrcpy(path, conn->connectpath);
312 fstrcat(path, dp.volumename);
313 if(is_msdfs_link(conn, path)) {
314 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
317 create_nondfs_path(pathname,&dp);
318 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
325 Special DFS redirect call for findfirst's.
326 If the findfirst is for the dfs junction, then no redirection,
327 if it is for the underlying directory contents, redirect.
330 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
336 pstrcpy(temp,pathname);
338 /* Is the path Dfs-redirectable? */
339 if(!dfs_redirect(temp,conn)) {
340 pstrcpy(pathname,temp);
344 parse_dfs_path(pathname,&dp);
345 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",
346 pathname,dp.restofthepath));
347 if(!(*(dp.restofthepath))) {
348 create_nondfs_path(pathname,&dp);
355 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
356 struct junction_map* junction,
359 char* pdata = *ppdata;
361 unsigned char uni_requestedpath[1024];
362 int uni_reqpathoffset1,uni_reqpathoffset2;
364 int requestedpathlen=0;
369 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
371 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
374 dump_data(10,uni_requestedpath,requestedpathlen);
376 DEBUG(10,("ref count = %u\n",junction->referral_count));
378 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
379 VERSION2_REFERRAL_SIZE * junction->referral_count;
381 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
383 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
385 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
386 2 * requestedpathlen;
387 DEBUG(10,("reply_size: %u\n",reply_size));
389 /* add up the unicode lengths of all the referral paths */
390 for(i=0;i<junction->referral_count;i++) {
391 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
392 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
395 DEBUG(10,("reply_size = %u\n",reply_size));
396 /* add the unexplained 0x16 bytes */
399 pdata = Realloc(pdata,reply_size);
401 DEBUG(0,("malloc failed for Realloc!\n"));
404 else *ppdata = pdata;
406 /* copy in the dfs requested paths.. required for offset calculations */
407 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
408 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
410 /* create the header */
411 SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
412 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
414 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
416 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
419 /* add the referral elements */
420 for(i=0;i<junction->referral_count;i++) {
421 struct referral* ref = &(junction->referral_list[i]);
424 SSVAL(pdata,offset,2); /* version 2 */
425 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
427 SSVAL(pdata,offset+4,1);
429 SSVAL(pdata,offset+4,0);
430 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
431 SIVAL(pdata,offset+8,ref->proximity);
432 SIVAL(pdata,offset+12,ref->ttl);
434 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
435 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
436 /* copy referred path into current offset */
437 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
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 SAFE_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 = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
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 = Realloc(pdata,reply_size);
481 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
484 else *ppdata = pdata;
486 /* create the header */
487 SSVAL(pdata,0,reqpathlen-2); /* path consumed */
488 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
490 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
492 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
494 /* copy in the reqpaths */
495 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
496 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
499 for(i=0;i<junction->referral_count;i++) {
500 struct referral* ref = &(junction->referral_list[i]);
503 SSVAL(pdata,offset,3); /* version 3 */
504 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
506 SSVAL(pdata,offset+4,1);
508 SSVAL(pdata,offset+4,0);
510 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
511 SIVAL(pdata,offset+8,ref->ttl);
513 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
514 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
515 /* copy referred path into current offset */
517 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
518 -1, STR_UNICODE|STR_TERMINATE);
519 SSVAL(pdata,offset+16,uni_curroffset-offset);
520 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
521 memset(pdata+offset+18,'\0',16);
523 uni_curroffset += unilen;
524 offset += VERSION3_REFERRAL_SIZE;
526 SAFE_FREE(junction->referral_list);
530 /******************************************************************
531 * Set up the Dfs referral for the dfs pathname
532 ******************************************************************/
534 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
536 struct junction_map junction;
542 ZERO_STRUCT(junction);
544 if(!create_junction(pathname, &junction))
547 /* get the junction entry */
548 if(!get_referred_path(&junction)) {
550 /* refer the same pathname, create a standard referral struct */
551 struct referral* ref;
552 self_referral = True;
553 junction.referral_count = 1;
554 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
555 DEBUG(0,("malloc failed for referral\n"));
559 pstrcpy(ref->alternate_path,pathname);
561 ref->ttl = REFERRAL_TTL;
562 junction.referral_list = ref;
564 self_referral = False;
565 if( DEBUGLVL( 3 ) ) {
567 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathname);
568 for(i=0;i<junction.referral_count;i++)
569 dbgtext(" %s",junction.referral_list[i].alternate_path);
574 /* create the referral depeding on version */
575 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
576 if(max_referral_level<2 || max_referral_level>3)
577 max_referral_level = 2;
579 switch(max_referral_level) {
582 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction, self_referral);
587 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction, self_referral);
592 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
597 DEBUG(10,("DFS Referral pdata:\n"));
598 dump_data(10,*ppdata,reply_size);
602 /**********************************************************************
603 The following functions are called by the NETDFS RPC pipe functions
604 **********************************************************************/
606 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
610 connection_struct conns;
611 connection_struct *conn = &conns;
614 if(!form_path_from_junction(jn, path, sizeof(path), conn))
617 /* form the msdfs_link contents */
618 pstrcpy(msdfs_link, "msdfs:");
619 for(i=0; i<jn->referral_count; i++) {
620 char* refpath = jn->referral_list[i].alternate_path;
622 trim_string(refpath, "\\", "\\");
627 pstrcat(msdfs_link, ",");
629 pstrcat(msdfs_link, refpath);
632 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
635 if(conn->vfs_ops.unlink(conn,path)!=0)
638 if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
639 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
640 path, msdfs_link, strerror(errno)));
646 BOOL remove_msdfs_link(struct junction_map* jn)
649 connection_struct conns;
650 connection_struct *conn = &conns;
652 if(!form_path_from_junction(jn, path, sizeof(path), conn))
655 if(conn->vfs_ops.unlink(conn, path)!=0)
661 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
666 pstring connect_path;
667 char* service_name = lp_servicename(snum);
668 connection_struct conns;
669 connection_struct *conn = &conns;
671 pstrcpy(connect_path,lp_pathname(snum));
673 if(*connect_path == '\0')
677 * Fake up a connection struct for the VFS layer.
680 if (!create_conn_struct(conn, snum, connect_path))
683 /* form a junction for the msdfs root - convention */
685 pstrpcy(jn[cnt].service_name, service_name);
686 jn[cnt].volume_name[0] = '\0';
687 jn[cnt].referral_count = 1;
689 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
693 dirp = conn->vfs_ops.opendir(conn, connect_path);
697 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
702 pstrcpy(pathreal, connect_path);
703 pstrcat(pathreal, "/");
704 pstrcat(pathreal, dname);
706 if(conn->vfs_ops.lstat(conn,pathreal,&st) != 0) {
707 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
710 if(S_ISLNK(st.st_mode)) {
711 buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
713 if(parse_symlink(buf, &(jn[cnt].referral_list), &(jn[cnt].referral_count))) {
714 pstrcpy(jn[cnt].service_name, service_name);
715 pstrcpy(jn[cnt].volume_name, dname);
721 conn->vfs_ops.closedir(conn,dirp);
726 int enum_msdfs_links(struct junction_map* jn)
734 for(i=0;*lp_servicename(i);i++) {
736 form_junctions(i,jn,&jn_count);