2 Unix SMB/CIFS implementation.
3 MSDfs services for Samba
4 Copyright (C) Shirish Kalele 2000
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 extern pstring global_myname;
25 /**********************************************************************
26 Create a tcon relative path from a dfs_path structure
27 **********************************************************************/
29 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
31 pstrcpy(pathname,pdp->volumename);
32 pstrcat(pathname,"\\");
33 pstrcat(pathname,pdp->restofthepath);
36 /**********************************************************************
37 Parse the pathname of the form \hostname\service\volume\restofthepath
38 into the dfs_path structure
39 **********************************************************************/
41 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
43 pstring pathname_local;
46 pstrcpy(pathname_local,pathname);
47 p = temp = pathname_local;
51 trim_string(temp,"\\","\\");
52 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
55 /* parse out hostname */
56 p = strchr_m(temp,'\\');
60 pstrcpy(pdp->hostname,temp);
61 DEBUG(10,("hostname: %s\n",pdp->hostname));
63 /* parse out servicename */
65 p = strchr_m(temp,'\\');
67 pstrcpy(pdp->servicename,temp);
71 pstrcpy(pdp->servicename,temp);
72 DEBUG(10,("servicename: %s\n",pdp->servicename));
74 /* parse out volumename */
76 p = strchr_m(temp,'\\');
78 pstrcpy(pdp->volumename,temp);
82 pstrcpy(pdp->volumename,temp);
83 DEBUG(10,("volumename: %s\n",pdp->volumename));
85 /* remaining path .. */
86 pstrcpy(pdp->restofthepath,p+1);
87 DEBUG(10,("rest of the path: %s\n",pdp->restofthepath));
91 /********************************************************
92 Fake up a connection struct for the VFS layer.
93 *********************************************************/
95 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
99 conn->connectpath = path;
101 if (!smbd_vfs_init(conn)) {
102 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
108 /**********************************************************************
109 Forms a valid Unix pathname from the junction
110 **********************************************************************/
112 static BOOL form_path_from_junction(struct junction_map* jn, char* path, int max_pathlen,
113 connection_struct *conn)
120 snum = lp_servicenumber(jn->service_name);
124 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
125 safe_strcat(path, "/", max_pathlen-1);
126 strlower(jn->volume_name);
127 safe_strcat(path, jn->volume_name, max_pathlen-1);
129 if (!create_conn_struct(conn, snum, path))
135 /**********************************************************************
136 Creates a junction structure from the Dfs pathname
137 **********************************************************************/
139 BOOL create_junction(char* pathname, struct junction_map* jn)
143 parse_dfs_path(pathname,&dp);
145 /* check if path is dfs : check hostname is the first token */
146 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0)) {
147 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n", dp.hostname, pathname));
151 /* Check for a non-DFS share */
152 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
153 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
157 pstrcpy(jn->service_name,dp.servicename);
158 pstrcpy(jn->volume_name,dp.volumename);
162 /**********************************************************************
163 Parse the contents of a symlink to verify if it is an msdfs referral
164 A valid referral is of the form: msdfs:server1\share1,server2\share2
165 **********************************************************************/
167 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
171 char* alt_path[MAX_REFERRAL_COUNT];
173 struct referral* reflist;
177 prot = strtok(temp,":");
179 if(!strequal(prot, "msdfs"))
182 /* It's an msdfs referral */
186 /* parse out the alternate paths */
187 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
190 DEBUG(10,("parse_symlink: count=%d\n", count));
192 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
193 if(reflist == NULL) {
194 DEBUG(0,("parse_symlink: Malloc failed!\n"));
198 for(i=0;i<count;i++) {
199 /* replace / in the alternate path by a \ */
200 char* p = strchr_m(alt_path[i],'/');
204 pstrcpy(reflist[i].alternate_path, "\\");
205 pstrcat(reflist[i].alternate_path, alt_path[i]);
206 reflist[i].proximity = 0;
207 reflist[i].ttl = REFERRAL_TTL;
208 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
217 /**********************************************************************
218 Returns true if the unix path is a valid msdfs symlink
219 **********************************************************************/
221 BOOL is_msdfs_link(connection_struct* conn, char* path)
225 int referral_len = 0;
232 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
233 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
237 if(S_ISLNK(st.st_mode)) {
238 /* open the link and read it */
239 referral_len = conn->vfs_ops.readlink(conn, path, referral, sizeof(pstring));
240 if(referral_len == -1)
241 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
243 referral[referral_len] = '\0';
244 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
245 if(parse_symlink(referral, NULL, NULL))
251 /**********************************************************************
252 Fills in the junction_map struct with the referrals from the
254 **********************************************************************/
256 BOOL get_referred_path(struct junction_map* junction)
261 connection_struct conns;
262 connection_struct *conn = &conns;
264 if(!form_path_from_junction(junction, path, sizeof(path), conn))
267 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
269 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
270 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
274 if(S_ISLNK(st.st_mode)) {
275 /* open the link and read it to get the dfs referral */
277 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
279 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
280 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
286 /**************************************************************
287 Decides if given pathname is Dfs and if it should be redirected
288 Converts pathname to non-dfs format if Dfs redirection not required
289 **************************************************************/
291 BOOL dfs_redirect(char* pathname, connection_struct* conn)
297 pstrcpy(temp,pathname);
299 if(!lp_msdfs_root(SNUM(conn)) )
302 parse_dfs_path(pathname,&dp);
304 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
307 /* check if need to redirect */
308 fstrcpy(path, conn->connectpath);
310 fstrcat(path, dp.volumename);
311 if(is_msdfs_link(conn, path)) {
312 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
315 create_nondfs_path(pathname,&dp);
316 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
323 Special DFS redirect call for findfirst's.
324 If the findfirst is for the dfs junction, then no redirection,
325 if it is for the underlying directory contents, redirect.
328 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
334 pstrcpy(temp,pathname);
336 /* Is the path Dfs-redirectable? */
337 if(!dfs_redirect(temp,conn)) {
338 pstrcpy(pathname,temp);
342 parse_dfs_path(pathname,&dp);
343 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",
344 pathname,dp.restofthepath));
345 if(!(*(dp.restofthepath))) {
346 create_nondfs_path(pathname,&dp);
353 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
354 struct junction_map* junction,
357 char* pdata = *ppdata;
359 unsigned char uni_requestedpath[1024];
360 int uni_reqpathoffset1,uni_reqpathoffset2;
362 int requestedpathlen=0;
367 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
369 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
372 dump_data(10,(const char *)uni_requestedpath,requestedpathlen);
374 DEBUG(10,("ref count = %u\n",junction->referral_count));
376 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
377 VERSION2_REFERRAL_SIZE * junction->referral_count;
379 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
381 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
383 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
384 2 * requestedpathlen;
385 DEBUG(10,("reply_size: %u\n",reply_size));
387 /* add up the unicode lengths of all the referral paths */
388 for(i=0;i<junction->referral_count;i++) {
389 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
390 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
393 DEBUG(10,("reply_size = %u\n",reply_size));
394 /* add the unexplained 0x16 bytes */
397 pdata = Realloc(pdata,reply_size);
399 DEBUG(0,("malloc failed for Realloc!\n"));
402 else *ppdata = pdata;
404 /* copy in the dfs requested paths.. required for offset calculations */
405 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
406 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
408 /* create the header */
409 SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
410 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
412 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
414 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
417 /* add the referral elements */
418 for(i=0;i<junction->referral_count;i++) {
419 struct referral* ref = &(junction->referral_list[i]);
422 SSVAL(pdata,offset,2); /* version 2 */
423 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
425 SSVAL(pdata,offset+4,1);
427 SSVAL(pdata,offset+4,0);
428 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
429 SIVAL(pdata,offset+8,ref->proximity);
430 SIVAL(pdata,offset+12,ref->ttl);
432 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
433 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
434 /* copy referred path into current offset */
435 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
437 SSVAL(pdata,offset+20,uni_curroffset-offset);
439 uni_curroffset += unilen;
440 offset += VERSION2_REFERRAL_SIZE;
442 /* add in the unexplained 22 (0x16) bytes at the end */
443 memset(pdata+uni_curroffset,'\0',0x16);
444 SAFE_FREE(junction->referral_list);
448 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
449 struct junction_map* junction,
452 char* pdata = *ppdata;
454 unsigned char uni_reqpath[1024];
455 int uni_reqpathoffset1, uni_reqpathoffset2;
462 DEBUG(10,("setting up version3 referral\n"));
464 reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
466 dump_data(10,(const char *)uni_reqpath,reqpathlen);
468 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
469 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
470 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
472 for(i=0;i<junction->referral_count;i++) {
473 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
474 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
477 pdata = Realloc(pdata,reply_size);
479 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
482 else *ppdata = pdata;
484 /* create the header */
485 SSVAL(pdata,0,reqpathlen-2); /* path consumed */
486 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
488 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
490 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
492 /* copy in the reqpaths */
493 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
494 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
497 for(i=0;i<junction->referral_count;i++) {
498 struct referral* ref = &(junction->referral_list[i]);
501 SSVAL(pdata,offset,3); /* version 3 */
502 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
504 SSVAL(pdata,offset+4,1);
506 SSVAL(pdata,offset+4,0);
508 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
509 SIVAL(pdata,offset+8,ref->ttl);
511 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
512 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
513 /* copy referred path into current offset */
515 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
516 -1, STR_UNICODE|STR_TERMINATE);
517 SSVAL(pdata,offset+16,uni_curroffset-offset);
518 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
519 memset(pdata+offset+18,'\0',16);
521 uni_curroffset += unilen;
522 offset += VERSION3_REFERRAL_SIZE;
524 SAFE_FREE(junction->referral_list);
528 /******************************************************************
529 * Set up the Dfs referral for the dfs pathname
530 ******************************************************************/
532 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
534 struct junction_map junction;
540 ZERO_STRUCT(junction);
542 if(!create_junction(pathname, &junction))
545 /* get the junction entry */
546 if(!get_referred_path(&junction)) {
548 /* refer the same pathname, create a standard referral struct */
549 struct referral* ref;
550 self_referral = True;
551 junction.referral_count = 1;
552 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
553 DEBUG(0,("malloc failed for referral\n"));
557 pstrcpy(ref->alternate_path,pathname);
559 ref->ttl = REFERRAL_TTL;
560 junction.referral_list = ref;
562 self_referral = False;
563 if( DEBUGLVL( 3 ) ) {
565 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathname);
566 for(i=0;i<junction.referral_count;i++)
567 dbgtext(" %s",junction.referral_list[i].alternate_path);
572 /* create the referral depeding on version */
573 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
574 if(max_referral_level<2 || max_referral_level>3)
575 max_referral_level = 2;
577 switch(max_referral_level) {
580 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction, self_referral);
585 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction, self_referral);
590 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
595 DEBUG(10,("DFS Referral pdata:\n"));
596 dump_data(10,*ppdata,reply_size);
600 /**********************************************************************
601 The following functions are called by the NETDFS RPC pipe functions
602 **********************************************************************/
604 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
608 connection_struct conns;
609 connection_struct *conn = &conns;
612 if(!form_path_from_junction(jn, path, sizeof(path), conn))
615 /* form the msdfs_link contents */
616 pstrcpy(msdfs_link, "msdfs:");
617 for(i=0; i<jn->referral_count; i++) {
618 char* refpath = jn->referral_list[i].alternate_path;
620 trim_string(refpath, "\\", "\\");
625 pstrcat(msdfs_link, ",");
627 pstrcat(msdfs_link, refpath);
630 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
633 if(conn->vfs_ops.unlink(conn,path)!=0)
636 if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
637 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
638 path, msdfs_link, strerror(errno)));
644 BOOL remove_msdfs_link(struct junction_map* jn)
647 connection_struct conns;
648 connection_struct *conn = &conns;
650 if(!form_path_from_junction(jn, path, sizeof(path), conn))
653 if(conn->vfs_ops.unlink(conn, path)!=0)
659 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
664 pstring connect_path;
665 char* service_name = lp_servicename(snum);
666 connection_struct conns;
667 connection_struct *conn = &conns;
669 pstrcpy(connect_path,lp_pathname(snum));
671 if(*connect_path == '\0')
675 * Fake up a connection struct for the VFS layer.
678 if (!create_conn_struct(conn, snum, connect_path))
681 /* form a junction for the msdfs root - convention */
683 pstrpcy(jn[cnt].service_name, service_name);
684 jn[cnt].volume_name[0] = '\0';
685 jn[cnt].referral_count = 1;
687 slprintf(alt_path,sizeof(alt_path)-1"\\\\%s\\%s", global_myname, service_name);
691 dirp = conn->vfs_ops.opendir(conn, connect_path);
695 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
700 pstrcpy(pathreal, connect_path);
701 pstrcat(pathreal, "/");
702 pstrcat(pathreal, dname);
704 if(conn->vfs_ops.lstat(conn,pathreal,&st) != 0) {
705 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
708 if(S_ISLNK(st.st_mode)) {
709 buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
711 if(parse_symlink(buf, &(jn[cnt].referral_list), &(jn[cnt].referral_count))) {
712 pstrcpy(jn[cnt].service_name, service_name);
713 pstrcpy(jn[cnt].volume_name, dname);
719 conn->vfs_ops.closedir(conn,dirp);
724 int enum_msdfs_links(struct junction_map* jn)
732 for(i=0;*lp_servicename(i);i++) {
734 form_junctions(i,jn,&jn_count);