perl script to autogenerate metrics.h file from profile.h
[Samba.git] / source / msdfs / msdfs.c
blob999ffa78b73725e4e06fb7f3b593bbfea17dc8a3
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
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.
22 #include "includes.h"
24 extern int DEBUGLEVEL;
25 extern pstring global_myname;
26 extern uint32 global_client_caps;
28 #ifdef WITH_MSDFS
30 /**********************************************************************
31 Create a tcon relative path from a dfs_path structure
32 **********************************************************************/
33 static void create_nondfs_path(char* pathname, struct dfs_path* pdp)
35 pstrcpy(pathname,pdp->volumename);
36 pstrcat(pathname,"\\");
37 pstrcat(pathname,pdp->restofthepath);
40 /**********************************************************************
41 Parse the pathname of the form \hostname\service\volume\restofthepath
42 into the dfs_path structure
43 **********************************************************************/
44 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
46 pstring pathname_local;
47 char* p,*temp;
49 pstrcpy(pathname_local,pathname);
50 p = temp = pathname_local;
52 ZERO_STRUCTP(pdp);
54 trim_string(temp,"\\","\\");
55 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
57 /* now tokenize */
58 /* parse out hostname */
59 p = strchr(temp,'\\');
60 if(p == NULL)
61 return False;
62 *p = '\0';
63 pstrcpy(pdp->hostname,temp);
64 DEBUG(10,("hostname: %s\n",pdp->hostname));
66 /* parse out servicename */
67 temp = p+1;
68 p = strchr(temp,'\\');
69 if(p == NULL)
71 pstrcpy(pdp->servicename,temp);
72 return True;
74 *p = '\0';
75 pstrcpy(pdp->servicename,temp);
76 DEBUG(10,("servicename: %s\n",pdp->servicename));
78 /* parse out volumename */
79 temp = p+1;
80 p = strchr(temp,'\\');
81 if(p == NULL)
83 pstrcpy(pdp->volumename,temp);
84 return True;
86 *p = '\0';
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));
93 return True;
96 /**********************************************************************
97 Forms a valid Unix pathname from the junction
98 **********************************************************************/
99 static BOOL form_path_from_junction(struct junction_map* jn, char* path,
100 int max_pathlen)
102 int snum;
104 if(!path || !jn)
105 return False;
107 snum = lp_servicenumber(jn->service_name);
108 if(snum < 0)
109 return False;
111 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
112 safe_strcat(path, "/", max_pathlen-1);
113 strlower(jn->volume_name);
114 safe_strcat(path, jn->volume_name, max_pathlen-1);
115 return True;
118 /**********************************************************************
119 Creates a junction structure from the Dfs pathname
120 **********************************************************************/
121 BOOL create_junction(char* pathname, struct junction_map* jn)
123 struct dfs_path dp;
125 parse_dfs_path(pathname,&dp);
127 /* check if path is dfs : check hostname is the first token */
128 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
130 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
131 dp.hostname, pathname));
132 return False;
135 /* Check for a non-DFS share */
136 if(!lp_msdfs_root(lp_servicenumber(dp.servicename)))
138 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
139 dp.servicename));
140 return False;
143 pstrcpy(jn->service_name,dp.servicename);
144 pstrcpy(jn->volume_name,dp.volumename);
145 return True;
148 /**********************************************************************
149 Parse the contents of a symlink to verify if it is an msdfs referral
150 A valid referral is of the form: msdfs:server1\share1,server2\share2
151 **********************************************************************/
152 static BOOL parse_symlink(char* buf,struct referral** preflist, int* refcount)
154 pstring temp;
155 char* prot;
156 char* alt_path[MAX_REFERRAL_COUNT];
157 int count=0, i;
158 struct referral* reflist;
160 pstrcpy(temp,buf);
162 prot = strtok(temp,":");
164 if(!strequal(prot, "msdfs"))
165 return False;
167 /* It's an msdfs referral */
168 if(!preflist)
169 return True;
171 /* parse out the alternate paths */
172 while(((alt_path[count] = strtok(NULL,",")) != NULL)
173 && count<MAX_REFERRAL_COUNT)
174 count++;
176 DEBUG(10,("parse_symlink: count=%d\n", count));
177 reflist = *preflist = (struct referral*) malloc(count *
178 sizeof(struct referral));
179 if(reflist == NULL)
181 DEBUG(0,("parse_symlink: Malloc failed!\n"));
182 return False;
185 for(i=0;i<count;i++)
187 /* replace / in the alternate path by a \ */
188 char* p = strchr(alt_path[i],'/');
189 if(p) *p = '\\';
191 pstrcpy(reflist[i].alternate_path, "\\");
192 pstrcat(reflist[i].alternate_path, alt_path[i]);
193 reflist[i].proximity = 0;
194 reflist[i].ttl = REFERRAL_TTL;
195 DEBUG(10, ("parse_symlink: Created alt path: %s\n",
196 reflist[i].alternate_path));
199 if(refcount)
200 *refcount = count;
202 return True;
205 /**********************************************************************
206 Returns true if the unix path is a valid msdfs symlink
207 **********************************************************************/
208 BOOL is_msdfs_link(connection_struct* conn, char* path)
210 SMB_STRUCT_STAT st;
211 pstring referral;
212 int referral_len = 0;
214 if(!path || !conn)
215 return False;
217 strlower(path);
219 if(conn->vfs_ops.lstat(conn,dos_to_unix(path,False),&st) != 0)
221 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
222 return False;
225 if(S_ISLNK(st.st_mode))
227 /* open the link and read it */
228 referral_len = readlink(path, referral, sizeof(pstring));
229 if(referral_len == -1)
230 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
231 path, strerror(errno)));
233 referral[referral_len] = '\0';
234 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
235 if(parse_symlink(referral, NULL, NULL))
236 return True;
238 return False;
241 /**********************************************************************
242 Fills in the junction_map struct with the referrals from the
243 symbolic link
244 **********************************************************************/
245 BOOL get_referred_path(struct junction_map* junction)
247 fstring path;
248 pstring buf;
249 SMB_STRUCT_STAT st;
251 if(!form_path_from_junction(junction, path, sizeof(path)))
252 return False;
254 DEBUG(5,("get_referred_path: lstat target: %s\n", path));
256 if(lstat(dos_to_unix(path, False),&st) != 0)
258 DEBUG(5,("get_referred_path: %s does not exist.\n",path));
259 return False;
262 if(S_ISLNK(st.st_mode))
264 /* open the link and read it to get the dfs referral */
265 readlink(path, buf, sizeof(buf));
266 DEBUG(5,("get_referred_path: Referral: %s\n",buf));
267 if(parse_symlink(buf, &junction->referral_list,
268 &junction->referral_count))
269 return True;
271 return False;
274 /**************************************************************
275 Decides if given pathname is Dfs and if it should be redirected
276 Converts pathname to non-dfs format if Dfs redirection not required
277 **************************************************************/
278 BOOL dfs_redirect(char* pathname, connection_struct* conn)
280 struct dfs_path dp;
281 pstring temp;
282 fstring path;
284 pstrcpy(temp,pathname);
286 if(!lp_msdfs_root(SNUM(conn)) )
287 return False;
289 parse_dfs_path(pathname,&dp);
291 if(global_myname && (strcasecmp(global_myname,dp.hostname)!=0))
292 return False;
294 /* check if need to redirect */
295 fstrcpy(path, conn->connectpath);
296 fstrcat(path, "/");
297 fstrcat(path, dp.volumename);
298 if(is_msdfs_link(conn, path))
300 DEBUG(4,("dfs_redirect: Redirecting %s\n",temp));
301 return True;
303 else
305 create_nondfs_path(pathname,&dp);
306 DEBUG(4,("dfs_redirect: Not redirecting %s. Converted to non-dfs pathname \'%s\'\n",
307 temp,pathname));
308 return False;
313 Special DFS redirect call for findfirst's.
314 If the findfirst is for the dfs junction, then no redirection,
315 if it is for the underlying directory contents, redirect.
317 BOOL dfs_findfirst_redirect(char* pathname, connection_struct* conn)
319 struct dfs_path dp;
321 pstring temp;
323 pstrcpy(temp,pathname);
325 /* Is the path Dfs-redirectable? */
326 if(!dfs_redirect(temp,conn))
328 pstrcpy(pathname,temp);
329 return False;
332 parse_dfs_path(pathname,&dp);
333 DEBUG(8,("dfs_findfirst_redirect: path %s is in Dfs. dp.restofthepath=.%s.\n",pathname,dp.restofthepath));
334 if(*(dp.restofthepath))
335 return True;
336 else
338 create_nondfs_path(pathname,&dp);
339 return False;
343 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
344 struct junction_map* junction,
345 BOOL self_referral)
347 char* pdata = *ppdata;
349 unsigned char uni_requestedpath[1024];
350 int uni_reqpathoffset1,uni_reqpathoffset2;
351 int uni_curroffset;
352 int requestedpathlen=0;
353 int offset;
354 int reply_size = 0;
355 int i=0;
357 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
359 requestedpathlen = (dos_struni2(uni_requestedpath,pathname,512)+1)*2;
361 dump_data(10,uni_requestedpath,requestedpathlen);
363 DEBUG(10,("ref count = %u\n",junction->referral_count));
365 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
366 VERSION2_REFERRAL_SIZE * junction->referral_count;
368 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
370 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
372 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
373 2 * requestedpathlen;
374 DEBUG(10,("reply_size: %u\n",reply_size));
376 /* add up the unicode lengths of all the referral paths */
377 for(i=0;i<junction->referral_count;i++)
379 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
380 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
383 DEBUG(10,("reply_size = %u\n",reply_size));
384 /* add the unexplained 0x16 bytes */
385 reply_size += 0x16;
387 pdata = *ppdata = Realloc(pdata,reply_size);
388 if(pdata == NULL)
390 DEBUG(0,("malloc failed for Realloc!\n"));
391 return -1;
394 /* copy in the dfs requested paths.. required for offset calculations */
395 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
396 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
399 /* create the header */
400 SSVAL(pdata,0,requestedpathlen-2); /* path consumed */
401 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
402 if(self_referral)
403 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
404 else
405 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
407 offset = 8;
408 /* add the referral elements */
409 for(i=0;i<junction->referral_count;i++)
411 struct referral* ref = &(junction->referral_list[i]);
412 int unilen;
414 SSVAL(pdata,offset,2); /* version 2 */
415 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
416 if(self_referral)
417 SSVAL(pdata,offset+4,1);
418 else
419 SSVAL(pdata,offset+4,0);
420 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
421 SIVAL(pdata,offset+8,ref->proximity);
422 SIVAL(pdata,offset+12,ref->ttl);
424 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
425 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
426 /* copy referred path into current offset */
427 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512)
428 +1)*2;
429 SSVAL(pdata,offset+20,uni_curroffset-offset);
431 uni_curroffset += unilen;
432 offset += VERSION2_REFERRAL_SIZE;
434 /* add in the unexplained 22 (0x16) bytes at the end */
435 memset(pdata+uni_curroffset,'\0',0x16);
436 free(junction->referral_list);
437 return reply_size;
440 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
441 struct junction_map* junction,
442 BOOL self_referral)
444 char* pdata = *ppdata;
446 unsigned char uni_reqpath[1024];
447 int uni_reqpathoffset1, uni_reqpathoffset2;
448 int uni_curroffset;
449 int reply_size = 0;
451 int reqpathlen = 0;
452 int offset,i=0;
454 DEBUG(10,("setting up version3 referral\n"));
456 reqpathlen = (dos_struni2(uni_reqpath,pathname,512)+1)*2;
458 dump_data(10,uni_reqpath,reqpathlen);
460 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE *
461 junction->referral_count;
462 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
463 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
465 for(i=0;i<junction->referral_count;i++)
467 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
468 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
471 pdata = *ppdata = Realloc(pdata,reply_size);
472 if(pdata == NULL)
474 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
475 return -1;
478 /* create the header */
479 SSVAL(pdata,0,reqpathlen-2); /* path consumed */
480 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
481 if(self_referral)
482 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
483 else
484 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
486 /* copy in the reqpaths */
487 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
488 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
490 offset = 8;
491 for(i=0;i<junction->referral_count;i++)
493 struct referral* ref = &(junction->referral_list[i]);
494 int unilen;
496 SSVAL(pdata,offset,3); /* version 3 */
497 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
498 if(self_referral)
499 SSVAL(pdata,offset+4,1);
500 else
501 SSVAL(pdata,offset+4,0);
503 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
504 SIVAL(pdata,offset+8,ref->ttl);
506 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
507 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
508 /* copy referred path into current offset */
509 unilen = (dos_struni2(pdata+uni_curroffset,ref->alternate_path,512)
510 +1)*2;
511 SSVAL(pdata,offset+16,uni_curroffset-offset);
512 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
513 memset(pdata+offset+18,'\0',16);
515 uni_curroffset += unilen;
516 offset += VERSION3_REFERRAL_SIZE;
518 free(junction->referral_list);
519 return reply_size;
524 /******************************************************************
525 * Set up the Dfs referral for the dfs pathname
526 ******************************************************************/
527 int setup_dfs_referral(char* pathname, int max_referral_level,
528 char** ppdata)
530 struct junction_map junction;
532 BOOL self_referral;
534 int reply_size = 0;
536 ZERO_STRUCT(junction);
538 if(!create_junction(pathname, &junction))
539 return -1;
541 /* get the junction entry */
542 if(!get_referred_path(&junction))
545 /* refer the same pathname, create a standard referral struct */
546 struct referral* ref;
547 self_referral = True;
548 junction.referral_count = 1;
549 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL)
551 DEBUG(0,("malloc failed for referral\n"));
552 return -1;
555 pstrcpy(ref->alternate_path,pathname);
556 ref->proximity = 0;
557 ref->ttl = REFERRAL_TTL;
558 junction.referral_list = ref;
560 else
562 self_referral = False;
563 if( DEBUGLVL( 3 ) )
565 int i=0;
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);
569 dbgtext(".\n");
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) max_referral_level = 2;
577 switch(max_referral_level)
579 case 2:
581 reply_size = setup_ver2_dfs_referral(pathname, ppdata, &junction,
582 self_referral);
583 break;
585 case 3:
587 reply_size = setup_ver3_dfs_referral(pathname, ppdata, &junction,
588 self_referral);
589 break;
591 default:
593 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n",
594 max_referral_level));
595 return -1;
599 DEBUG(10,("DFS Referral pdata:\n"));
600 dump_data(10,*ppdata,reply_size);
601 return reply_size;
604 int dfs_path_error(char* inbuf, char* outbuf)
606 enum remote_arch_types ra_type = get_remote_arch();
607 BOOL NT_arch = ((ra_type==RA_WINNT) || (ra_type == RA_WIN2K));
608 if(NT_arch && (global_client_caps & (CAP_NT_SMBS | CAP_STATUS32)) )
610 SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
611 return(ERROR(0,0xc0000000|NT_STATUS_PATH_NOT_COVERED));
613 return(ERROR(ERRSRV,ERRbadpath));
616 /**********************************************************************
617 The following functions are called by the NETDFS RPC pipe functions
618 **********************************************************************/
619 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
621 pstring path;
622 pstring msdfs_link;
623 int i=0;
625 if(!form_path_from_junction(jn, path, sizeof(path)))
626 return False;
628 /* form the msdfs_link contents */
629 pstrcpy(msdfs_link, "msdfs:");
630 for(i=0; i<jn->referral_count; i++)
632 char* refpath = jn->referral_list[i].alternate_path;
634 trim_string(refpath, "\\", "\\");
635 if(*refpath == '\0')
636 continue;
638 if(i>0)
639 pstrcat(msdfs_link, ",");
641 pstrcat(msdfs_link, refpath);
644 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
645 path, msdfs_link));
647 if(exists)
648 if(unlink(path)!=0)
649 return False;
651 if(symlink(msdfs_link, path) < 0)
653 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
654 path, msdfs_link, strerror(errno)));
655 return False;
657 return True;
660 BOOL remove_msdfs_link(struct junction_map* jn)
662 pstring path;
664 if(!form_path_from_junction(jn, path, sizeof(path)))
665 return False;
667 if(unlink(path)!=0)
668 return False;
670 return True;
673 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
675 int cnt = *jn_count;
676 DIR *dirp;
677 char* dname;
679 char* connect_path = lp_pathname(snum);
680 char* service_name = lp_servicename(snum);
682 if(*connect_path == '\0')
683 return False;
685 /* form a junction for the msdfs root - convention */
687 pstrpcy(jn[cnt].service_name, service_name);
688 jn[cnt].volume_name[0] = '\0';
689 jn[cnt].referral_count = 1;
691 slprintf(alt_path,"\\\\%s\\%s", global_myname, service_name);
692 jn[cnt].referral_l
695 dirp = opendir(connect_path);
696 if(!dirp)
697 return False;
699 while((dname = readdirname(dirp)) != NULL)
701 SMB_STRUCT_STAT st;
702 pstring pathreal;
703 fstring buf;
704 int buflen = 0;
705 pstrcpy(pathreal, connect_path);
706 pstrcat(pathreal, "/");
707 pstrcat(pathreal, dname);
709 if(lstat(pathreal,&st) != 0)
711 DEBUG(4,("lstat error for %s: %s\n",pathreal, strerror(errno)));
712 continue;
714 if(S_ISLNK(st.st_mode))
716 buflen = readlink(pathreal, buf, sizeof(fstring));
717 buf[buflen] = '\0';
718 if(parse_symlink(buf, &(jn[cnt].referral_list),
719 &(jn[cnt].referral_count)))
721 pstrcpy(jn[cnt].service_name, service_name);
722 pstrcpy(jn[cnt].volume_name, dname);
723 cnt++;
728 closedir(dirp);
729 *jn_count = cnt;
730 return True;
733 int enum_msdfs_links(struct junction_map* jn)
735 int i=0;
736 int jn_count = 0;
738 if(!lp_host_msdfs())
739 return -1;
741 for(i=0;*lp_servicename(i);i++)
743 if(lp_msdfs_root(i))
744 form_junctions(i,jn,&jn_count);
746 return jn_count;
750 #else
751 /* Stub functions if WITH_MSDFS not defined */
752 int setup_dfs_referral(char* pathname, int max_referral_level,
753 char** ppdata)
755 return -1;
758 BOOL is_msdfs_link(connection_struct* conn, char* path)
760 return False;
763 #endif