merge from APPLIANCE_HEAD
[Samba.git] / source / msdfs / msdfs.c
blob5ddf7f84f8b3d7df70df559f4b1914f6a07a48f0
1 /*
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.
21 #include "includes.h"
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;
44 char* p,*temp;
46 pstrcpy(pathname_local,pathname);
47 p = temp = pathname_local;
49 ZERO_STRUCTP(pdp);
51 trim_string(temp,"\\","\\");
52 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
54 /* now tokenize */
55 /* parse out hostname */
56 p = strchr_m(temp,'\\');
57 if(p == NULL)
58 return False;
59 *p = '\0';
60 pstrcpy(pdp->hostname,temp);
61 DEBUG(10,("hostname: %s\n",pdp->hostname));
63 /* parse out servicename */
64 temp = p+1;
65 p = strchr_m(temp,'\\');
66 if(p == NULL) {
67 pstrcpy(pdp->servicename,temp);
68 return True;
70 *p = '\0';
71 pstrcpy(pdp->servicename,temp);
72 DEBUG(10,("servicename: %s\n",pdp->servicename));
74 /* parse out volumename */
75 temp = p+1;
76 p = strchr_m(temp,'\\');
77 if(p == NULL) {
78 pstrcpy(pdp->volumename,temp);
79 return True;
81 *p = '\0';
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));
88 return True;
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)
97 ZERO_STRUCTP(conn);
98 conn->service = snum;
99 conn->connectpath = path;
101 if (!smbd_vfs_init(conn)) {
102 DEBUG(0,("create_conn_struct: vfs init failed.\n"));
103 return False;
105 return True;
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)
115 int snum;
117 if(!path || !jn)
118 return False;
120 snum = lp_servicenumber(jn->service_name);
121 if(snum < 0)
122 return False;
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))
130 return False;
132 return True;
135 /**********************************************************************
136 Creates a junction structure from the Dfs pathname
137 **********************************************************************/
139 BOOL create_junction(char* pathname, struct junction_map* jn)
141 struct dfs_path dp;
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));
148 return False;
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));
154 return False;
157 pstrcpy(jn->service_name,dp.servicename);
158 pstrcpy(jn->volume_name,dp.volumename);
159 return True;
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)
169 pstring temp;
170 char* prot;
171 char* alt_path[MAX_REFERRAL_COUNT];
172 int count=0, i;
173 struct referral* reflist;
175 pstrcpy(temp,buf);
177 prot = strtok(temp,":");
179 if(!strequal(prot, "msdfs"))
180 return False;
182 /* It's an msdfs referral */
183 if(!preflist)
184 return True;
186 /* parse out the alternate paths */
187 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
188 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"));
195 return False;
198 for(i=0;i<count;i++) {
199 /* replace / in the alternate path by a \ */
200 char* p = strchr_m(alt_path[i],'/');
201 if(p)
202 *p = '\\';
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));
211 if(refcount)
212 *refcount = count;
214 return True;
217 /**********************************************************************
218 Returns true if the unix path is a valid msdfs symlink
219 **********************************************************************/
221 BOOL is_msdfs_link(connection_struct* conn, char* path)
223 SMB_STRUCT_STAT st;
224 pstring referral;
225 int referral_len = 0;
227 if(!path || !conn)
228 return False;
230 strlower(path);
232 if(conn->vfs_ops.lstat(conn,path,&st) != 0) {
233 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
234 return False;
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))
246 return True;
248 return False;
251 /**********************************************************************
252 Fills in the junction_map struct with the referrals from the
253 symbolic link
254 **********************************************************************/
256 BOOL get_referred_path(struct junction_map* junction)
258 pstring path;
259 pstring buf;
260 SMB_STRUCT_STAT st;
261 connection_struct conns;
262 connection_struct *conn = &conns;
264 if(!form_path_from_junction(junction, path, sizeof(path), conn))
265 return False;
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));
271 return False;
274 if(S_ISLNK(st.st_mode)) {
275 /* open the link and read it to get the dfs referral */
276 int linkcnt = 0;
277 linkcnt = conn->vfs_ops.readlink(conn, path, buf, sizeof(buf));
278 buf[linkcnt] = '\0';
279 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
280 if(parse_symlink(buf, &junction->referral_list, &junction->referral_count))
281 return True;
283 return False;
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)
293 struct dfs_path dp;
294 pstring temp;
295 fstring path;
297 pstrcpy(temp,pathname);
299 if(!lp_msdfs_root(SNUM(conn)) )
300 return False;
302 parse_dfs_path(pathname,&dp);
304 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
305 return False;
307 /* check if need to redirect */
308 fstrcpy(path, conn->connectpath);
309 fstrcat(path, "/");
310 fstrcat(path, dp.volumename);
311 if(is_msdfs_link(conn, path)) {
312 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
313 return True;
314 } else {
315 create_nondfs_path(pathname,&dp);
316 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
317 temp,pathname));
318 return False;
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)
330 struct dfs_path dp;
332 pstring temp;
334 pstrcpy(temp,pathname);
336 /* Is the path Dfs-redirectable? */
337 if(!dfs_redirect(temp,conn)) {
338 pstrcpy(pathname,temp);
339 return False;
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);
347 return False;
350 return True;
353 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
354 struct junction_map* junction,
355 BOOL self_referral)
357 char* pdata = *ppdata;
359 unsigned char uni_requestedpath[1024];
360 int uni_reqpathoffset1,uni_reqpathoffset2;
361 int uni_curroffset;
362 int requestedpathlen=0;
363 int offset;
364 int reply_size = 0;
365 int i=0;
367 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
369 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
370 STR_TERMINATE);
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 */
395 reply_size += 0x16;
397 pdata = Realloc(pdata,reply_size);
398 if(pdata == NULL) {
399 DEBUG(0,("malloc failed for Realloc!\n"));
400 return -1;
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 */
411 if(self_referral)
412 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
413 else
414 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
416 offset = 8;
417 /* add the referral elements */
418 for(i=0;i<junction->referral_count;i++) {
419 struct referral* ref = &(junction->referral_list[i]);
420 int unilen;
422 SSVAL(pdata,offset,2); /* version 2 */
423 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
424 if(self_referral)
425 SSVAL(pdata,offset+4,1);
426 else
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,
436 -1, STR_UNICODE);
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);
445 return reply_size;
448 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
449 struct junction_map* junction,
450 BOOL self_referral)
452 char* pdata = *ppdata;
454 unsigned char uni_reqpath[1024];
455 int uni_reqpathoffset1, uni_reqpathoffset2;
456 int uni_curroffset;
457 int reply_size = 0;
459 int reqpathlen = 0;
460 int offset,i=0;
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);
478 if(pdata == NULL) {
479 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
480 return -1;
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 */
487 if(self_referral)
488 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
489 else
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);
496 offset = 8;
497 for(i=0;i<junction->referral_count;i++) {
498 struct referral* ref = &(junction->referral_list[i]);
499 int unilen;
501 SSVAL(pdata,offset,3); /* version 3 */
502 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
503 if(self_referral)
504 SSVAL(pdata,offset+4,1);
505 else
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);
525 return reply_size;
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;
536 BOOL self_referral;
538 int reply_size = 0;
540 ZERO_STRUCT(junction);
542 if(!create_junction(pathname, &junction))
543 return -1;
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"));
554 return -1;
557 pstrcpy(ref->alternate_path,pathname);
558 ref->proximity = 0;
559 ref->ttl = REFERRAL_TTL;
560 junction.referral_list = ref;
561 } else {
562 self_referral = False;
563 if( DEBUGLVL( 3 ) ) {
564 int i=0;
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);
568 dbgtext(".\n");
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) {
578 case 2:
580 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction, self_referral);
581 break;
583 case 3:
585 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction, self_referral);
586 break;
588 default:
590 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
591 return -1;
595 DEBUG(10,("DFS Referral pdata:\n"));
596 dump_data(10,*ppdata,reply_size);
597 return 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)
606 pstring path;
607 pstring msdfs_link;
608 connection_struct conns;
609 connection_struct *conn = &conns;
610 int i=0;
612 if(!form_path_from_junction(jn, path, sizeof(path), conn))
613 return False;
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, "\\", "\\");
621 if(*refpath == '\0')
622 continue;
624 if(i>0)
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));
632 if(exists)
633 if(conn->vfs_ops.unlink(conn,path)!=0)
634 return False;
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)));
639 return False;
641 return True;
644 BOOL remove_msdfs_link(struct junction_map* jn)
646 pstring path;
647 connection_struct conns;
648 connection_struct *conn = &conns;
650 if(!form_path_from_junction(jn, path, sizeof(path), conn))
651 return False;
653 if(conn->vfs_ops.unlink(conn, path)!=0)
654 return False;
656 return True;
659 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
661 int cnt = *jn_count;
662 DIR *dirp;
663 char* dname;
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')
672 return False;
675 * Fake up a connection struct for the VFS layer.
678 if (!create_conn_struct(conn, snum, connect_path))
679 return False;
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);
688 jn[cnt].referral_l
691 dirp = conn->vfs_ops.opendir(conn, connect_path);
692 if(!dirp)
693 return False;
695 while((dname = vfs_readdirname(conn, dirp)) != NULL) {
696 SMB_STRUCT_STAT st;
697 pstring pathreal;
698 fstring buf;
699 int buflen = 0;
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)));
706 continue;
708 if(S_ISLNK(st.st_mode)) {
709 buflen = conn->vfs_ops.readlink(conn, pathreal, buf, sizeof(fstring));
710 buf[buflen] = '\0';
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);
714 cnt++;
719 conn->vfs_ops.closedir(conn,dirp);
720 *jn_count = cnt;
721 return True;
724 int enum_msdfs_links(struct junction_map* jn)
726 int i=0;
727 int jn_count = 0;
729 if(!lp_host_msdfs())
730 return -1;
732 for(i=0;*lp_servicename(i);i++) {
733 if(lp_msdfs_root(i))
734 form_junctions(i,jn,&jn_count);
736 return jn_count;