Ok - this is a big patch - and it may break smbd a bit (although
[ira/wip.git] / source3 / smbd / vfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    VFS initialisation and support functions
5    Copyright (C) Tim Potter 1999
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 /* Some structures to help us initialise the vfs operations table */
27
28 struct vfs_syminfo {
29         char *name;
30         void *fptr;
31 };
32
33 /* Default vfs hooks.  WARNING: The order of these initialisers is
34    very important.  They must be in the same order as defined in
35    vfs.h.  Change at your own peril. */
36
37 struct vfs_ops default_vfs_ops = {
38
39         /* Disk operations */        
40
41         vfswrap_dummy_connect,
42         vfswrap_dummy_disconnect,
43         vfswrap_disk_free,
44
45         /* Directory operations */
46
47         vfswrap_opendir,
48         vfswrap_readdir,
49         vfswrap_mkdir,
50         vfswrap_rmdir,
51         vfswrap_closedir,
52
53         /* File operations */
54
55         vfswrap_open,
56         vfswrap_close,
57         vfswrap_read,
58         vfswrap_write,
59         vfswrap_lseek,
60         vfswrap_rename,
61         vfswrap_fsync,
62         vfswrap_stat,
63         vfswrap_fstat,
64         vfswrap_lstat,
65         vfswrap_unlink,
66         vfswrap_chmod,
67         vfswrap_chown,
68         vfswrap_chdir,
69         vfswrap_getwd,
70         vfswrap_utime,
71         vfswrap_ftruncate,
72         vfswrap_lock,
73         vfswrap_fget_nt_acl,
74         vfswrap_get_nt_acl,
75         vfswrap_fset_nt_acl,
76         vfswrap_set_nt_acl
77 };
78
79 /****************************************************************************
80   initialise default vfs hooks
81 ****************************************************************************/
82 int vfs_init_default(connection_struct *conn)
83 {
84     DEBUG(3, ("Initialising default vfs hooks\n"));
85
86     memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
87     return True;
88 }
89
90 /****************************************************************************
91   initialise custom vfs hooks
92 ****************************************************************************/
93
94 #ifdef HAVE_LIBDL
95 BOOL vfs_init_custom(connection_struct *conn)
96 {
97     struct vfs_ops *ops, *(*fptr)(struct vfs_options *options);
98
99     DEBUG(3, ("Initialising custom vfs hooks from %s\n",
100               lp_vfsobj(SNUM(conn))));
101
102     /* Open object file */
103
104     if ((conn->dl_handle = dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) {
105                 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), dlerror()));
106                 return False;
107     }
108
109     /* Get handle on vfs_init() symbol */
110
111     fptr = (struct vfs_ops *(*)(struct vfs_options *))
112             dlsym(conn->dl_handle, "vfs_init");
113
114     if (fptr == NULL) {
115         DEBUG(0, ("No vfs_init() symbol found in %s\n", 
116                   lp_vfsobj(SNUM(conn))));
117         return False;
118     }
119
120     /* Initialise vfs_ops structure */
121
122     if ((ops = fptr(NULL)) == NULL) {
123         DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
124         return False;
125     }
126
127     /* Fill in unused operations with default (disk based) ones.
128        There's probably a neater way to do this then a whole bunch of
129        if statements. */ 
130
131     memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
132     
133     if (conn->vfs_ops.connect == NULL) {
134         conn->vfs_ops.connect = default_vfs_ops.connect;
135     }
136
137     if (conn->vfs_ops.disconnect == NULL) {
138         conn->vfs_ops.disconnect = default_vfs_ops.disconnect;
139     }
140
141     if (conn->vfs_ops.disk_free == NULL) {
142         conn->vfs_ops.disk_free = default_vfs_ops.disk_free;
143     }
144
145     if (conn->vfs_ops.opendir == NULL) {
146         conn->vfs_ops.opendir = default_vfs_ops.opendir;
147     }
148
149     if (conn->vfs_ops.readdir == NULL) {
150         conn->vfs_ops.readdir = default_vfs_ops.readdir;
151     }
152
153     if (conn->vfs_ops.mkdir == NULL) {
154         conn->vfs_ops.mkdir = default_vfs_ops.mkdir;
155     }
156
157     if (conn->vfs_ops.rmdir == NULL) {
158         conn->vfs_ops.rmdir = default_vfs_ops.rmdir;
159     }
160
161     if (conn->vfs_ops.closedir == NULL) {
162         conn->vfs_ops.closedir = default_vfs_ops.closedir;
163     }
164
165     if (conn->vfs_ops.open == NULL) {
166         conn->vfs_ops.open = default_vfs_ops.open;
167     }
168
169     if (conn->vfs_ops.close == NULL) {
170         conn->vfs_ops.close = default_vfs_ops.close;
171     }
172
173     if (conn->vfs_ops.read == NULL) {
174         conn->vfs_ops.read = default_vfs_ops.read;
175     }
176     
177     if (conn->vfs_ops.write == NULL) {
178         conn->vfs_ops.write = default_vfs_ops.write;
179     }
180     
181     if (conn->vfs_ops.lseek == NULL) {
182         conn->vfs_ops.lseek = default_vfs_ops.lseek;
183     }
184     
185     if (conn->vfs_ops.rename == NULL) {
186         conn->vfs_ops.rename = default_vfs_ops.rename;
187     }
188     
189     if (conn->vfs_ops.fsync == NULL) {
190         conn->vfs_ops.fsync = default_vfs_ops.fsync;
191     }
192     
193     if (conn->vfs_ops.stat == NULL) {
194         conn->vfs_ops.stat = default_vfs_ops.stat;
195     }
196     
197     if (conn->vfs_ops.fstat == NULL) {
198         conn->vfs_ops.fstat = default_vfs_ops.fstat;
199     }
200     
201     if (conn->vfs_ops.lstat == NULL) {
202         conn->vfs_ops.lstat = default_vfs_ops.lstat;
203     }
204     
205     if (conn->vfs_ops.unlink == NULL) {
206         conn->vfs_ops.unlink = default_vfs_ops.unlink;
207     }
208     
209     if (conn->vfs_ops.chmod == NULL) {
210         conn->vfs_ops.chmod = default_vfs_ops.chmod;
211     }
212     
213     if (conn->vfs_ops.chown == NULL) {
214         conn->vfs_ops.chown = default_vfs_ops.chown;
215     }
216     
217     if (conn->vfs_ops.chdir == NULL) {
218         conn->vfs_ops.chdir = default_vfs_ops.chdir;
219     }
220
221     if (conn->vfs_ops.getwd == NULL) {
222         conn->vfs_ops.getwd = default_vfs_ops.getwd;
223     }
224     
225     if (conn->vfs_ops.utime == NULL) {
226         conn->vfs_ops.utime = default_vfs_ops.utime;
227     }
228     
229     if (conn->vfs_ops.ftruncate == NULL) {
230         conn->vfs_ops.ftruncate = default_vfs_ops.ftruncate;
231     }
232     
233     if (conn->vfs_ops.lock == NULL) {
234         conn->vfs_ops.lock = default_vfs_ops.lock;
235     }
236
237     if (conn->vfs_ops.fget_nt_acl == NULL) {
238         conn->vfs_ops.fget_nt_acl = default_vfs_ops.fget_nt_acl;
239     }
240
241     if (conn->vfs_ops.get_nt_acl == NULL) {
242         conn->vfs_ops.get_nt_acl = default_vfs_ops.get_nt_acl;
243     }
244
245     if (conn->vfs_ops.fset_nt_acl == NULL) {
246         conn->vfs_ops.fset_nt_acl = default_vfs_ops.fset_nt_acl;
247     }
248
249     if (conn->vfs_ops.set_nt_acl == NULL) {
250         conn->vfs_ops.set_nt_acl = default_vfs_ops.set_nt_acl;
251     }
252     
253     return True;
254 }
255 #endif
256
257 /*******************************************************************
258  Check if directory exists.
259 ********************************************************************/
260
261 BOOL vfs_directory_exist(connection_struct *conn, char *dname, SMB_STRUCT_STAT *st)
262 {
263         SMB_STRUCT_STAT st2;
264         BOOL ret;
265
266         if (!st)
267                 st = &st2;
268
269         if (vfs_stat(conn,dname,st) != 0) 
270                 return(False);
271
272         ret = S_ISDIR(st->st_mode);
273         if(!ret)
274                 errno = ENOTDIR;
275
276   return ret;
277 }
278
279 /*******************************************************************
280  vfs mkdir wrapper that calls dos_to_unix.
281 ********************************************************************/
282
283 int vfs_mkdir(connection_struct *conn, char *fname, mode_t mode)
284 {
285         int ret;
286         pstring name;
287         SMB_STRUCT_STAT sbuf;
288
289         pstrcpy(name,dos_to_unix(fname,False)); /* paranoia copy */
290         if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
291                 /* 
292                  * Check if high bits should have been set,
293                  * then (if bits are missing): add them.
294                  * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
295                  */
296                 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
297                                 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
298                         vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
299         }
300         return ret;
301 }
302
303 /*******************************************************************
304  vfs getwd wrapper that calls dos_to_unix.
305 ********************************************************************/
306
307 char *vfs_getwd(connection_struct *conn, char *unix_path)
308 {
309     char *wd;
310     wd = conn->vfs_ops.getwd(conn,unix_path);
311     if (wd)
312         unix_to_dos(wd, True);
313     return wd;
314 }
315
316 /*******************************************************************
317  Check if a vfs file exists.
318 ********************************************************************/
319
320 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
321 {
322         SMB_STRUCT_STAT st;
323
324         if (!sbuf)
325                 sbuf = &st;
326
327         ZERO_STRUCTP(sbuf);
328  
329         if (vfs_stat(conn,fname,sbuf) != 0) 
330                 return(False);
331
332         return(S_ISREG(sbuf->st_mode));
333 }
334
335 /****************************************************************************
336  Write data to a fd on the vfs.
337 ****************************************************************************/
338
339 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
340 {
341   size_t total=0;
342   ssize_t ret;
343
344   while (total < N)
345   {
346     ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
347
348     if (ret == -1) return -1;
349     if (ret == 0) return total;
350
351     total += ret;
352   }
353   return (ssize_t)total;
354 }
355
356 /****************************************************************************
357  Transfer some data between two file_struct's.
358 ****************************************************************************/
359
360 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp, 
361                             int out_fd, files_struct *out_fsp,
362                             SMB_OFF_T n, char *header, int headlen, int align)
363 {
364   static char *buf=NULL;  
365   static int size=0;
366   char *buf1,*abuf;
367   SMB_OFF_T total = 0;
368
369   DEBUG(4,("vfs_transfer_file n=%.0f  (head=%d) called\n",(double)n,headlen));
370
371   /* Check we have at least somewhere to read from */
372
373   SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
374
375   if (size == 0) {
376     size = lp_readsize();
377     size = MAX(size,1024);
378   }
379
380   while (!buf && size>0) {
381     buf = (char *)Realloc(buf,size+8);
382     if (!buf) size /= 2;
383   }
384
385   if (!buf) {
386     DEBUG(0,("Can't allocate transfer buffer!\n"));
387     exit(1);
388   }
389
390   abuf = buf + (align%8);
391
392   if (header)
393     n += headlen;
394
395   while (n > 0)
396   {
397     int s = (int)MIN(n,(SMB_OFF_T)size);
398     int ret,ret2=0;
399
400     ret = 0;
401
402     if (header && (headlen >= MIN(s,1024))) {
403       buf1 = header;
404       s = headlen;
405       ret = headlen;
406       headlen = 0;
407       header = NULL;
408     } else {
409       buf1 = abuf;
410     }
411
412     if (header && headlen > 0)
413     {
414       ret = MIN(headlen,size);
415       memcpy(buf1,header,ret);
416       headlen -= ret;
417       header += ret;
418       if (headlen <= 0) header = NULL;
419     }
420
421     if (s > ret) {
422       ret += in_fsp ? 
423           in_fsp->conn->vfs_ops.read(in_fsp,in_fsp->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
424     }
425
426     if (ret > 0) {
427                 if (out_fsp)
428                     ret2 = out_fsp->conn->vfs_ops.write(out_fsp,out_fsp->fd,buf1,ret);
429                 else
430                     ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
431     }
432
433       if (ret2 > 0) total += ret2;
434       /* if we can't write then dump excess data */
435       if (ret2 != ret)
436         vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
437
438     if (ret <= 0 || ret2 != ret)
439       return(total);
440     n -= ret;
441   }
442   return(total);
443 }
444
445 /*******************************************************************
446  A vfs_readdir wrapper which just returns the file name.
447 ********************************************************************/
448
449 char *vfs_readdirname(connection_struct *conn, void *p)
450 {
451         struct dirent *ptr;
452         char *dname;
453
454         if (!p)
455                 return(NULL);
456   
457         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
458         if (!ptr)
459                 return(NULL);
460
461         dname = ptr->d_name;
462
463 #ifdef NEXT2
464         if (telldir(p) < 0)
465                 return(NULL);
466 #endif
467
468 #ifdef HAVE_BROKEN_READDIR
469         /* using /usr/ucb/cc is BAD */
470         dname = dname - 2;
471 #endif
472
473         {
474                 static pstring buf;
475                 memcpy(buf, dname, NAMLEN(ptr)+1);
476                 unix_to_dos(buf, True);
477                 dname = buf;
478         }
479
480         return(dname);
481 }
482
483 /* VFS options not quite working yet */
484
485 #if 0
486
487 /***************************************************************************
488   handle the interpretation of the vfs option parameter
489  *************************************************************************/
490 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
491 {
492     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
493     int i;
494     
495     /* Create new vfs option */
496
497     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
498     if (new_option == NULL) {
499         return False;
500     }
501
502     ZERO_STRUCTP(new_option);
503
504     /* Get name and value */
505     
506     new_option->name = strtok(pszParmValue, "=");
507
508     if (new_option->name == NULL) {
509         return False;
510     }
511
512     while(isspace(*new_option->name)) {
513         new_option->name++;
514     }
515
516     for (i = strlen(new_option->name); i > 0; i--) {
517         if (!isspace(new_option->name[i - 1])) break;
518     }
519
520     new_option->name[i] = '\0';
521     new_option->name = strdup(new_option->name);
522
523     new_option->value = strtok(NULL, "=");
524
525     if (new_option->value != NULL) {
526
527         while(isspace(*new_option->value)) {
528             new_option->value++;
529         }
530         
531         for (i = strlen(new_option->value); i > 0; i--) {
532             if (!isspace(new_option->value[i - 1])) break;
533         }
534         
535         new_option->value[i] = '\0';
536         new_option->value = strdup(new_option->value);
537     }
538
539     /* Add to list */
540
541     DLIST_ADD(*options, new_option);
542
543     return True;
544 }
545
546 #endif
547
548
549 /*******************************************************************
550  A wrapper for vfs_chdir().
551 ********************************************************************/
552
553 int vfs_ChDir(connection_struct *conn, char *path)
554 {
555         int res;
556         static pstring LastDir="";
557
558         if (strcsequal(path,"."))
559                 return(0);
560
561         if (*path == '/' && strcsequal(LastDir,path))
562                 return(0);
563
564         DEBUG(3,("vfs_ChDir to %s\n",path));
565
566         res = vfs_chdir(conn,path);
567         if (!res)
568                 pstrcpy(LastDir,path);
569         return(res);
570 }
571
572 /* number of list structures for a caching GetWd function. */
573 #define MAX_GETWDCACHE (50)
574
575 struct
576 {
577   SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
578   SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
579   char *dos_path; /* The pathname in DOS format. */
580   BOOL valid;
581 } ino_list[MAX_GETWDCACHE];
582
583 extern BOOL use_getwd_cache;
584
585 /****************************************************************************
586  Prompte a ptr (to make it recently used)
587 ****************************************************************************/
588
589 static void array_promote(char *array,int elsize,int element)
590 {
591         char *p;
592         if (element == 0)
593                 return;
594
595         p = (char *)malloc(elsize);
596
597         if (!p) {
598                 DEBUG(5,("array_promote: malloc fail\n"));
599                 return;
600         }
601
602         memcpy(p,array + element * elsize, elsize);
603         memmove(array + elsize,array,elsize*element);
604         memcpy(array,p,elsize);
605         free(p);
606 }
607
608 /*******************************************************************
609  Return the absolute current directory path - given a UNIX pathname.
610  Note that this path is returned in DOS format, not UNIX
611  format. Note this can be called with conn == NULL.
612 ********************************************************************/
613
614 char *vfs_GetWd(connection_struct *conn, char *path)
615 {
616   pstring s;
617   static BOOL getwd_cache_init = False;
618   SMB_STRUCT_STAT st, st2;
619   int i;
620
621   *s = 0;
622
623   if (!use_getwd_cache)
624     return(vfs_getwd(conn,path));
625
626   /* init the cache */
627   if (!getwd_cache_init)
628   {
629     getwd_cache_init = True;
630     for (i=0;i<MAX_GETWDCACHE;i++)
631     {
632       string_set(&ino_list[i].dos_path,"");
633       ino_list[i].valid = False;
634     }
635   }
636
637   /*  Get the inode of the current directory, if this doesn't work we're
638       in trouble :-) */
639
640   if (vfs_stat(conn, ".",&st) == -1)
641   {
642     DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
643     return(vfs_getwd(conn,path));
644   }
645
646
647   for (i=0; i<MAX_GETWDCACHE; i++)
648     if (ino_list[i].valid)
649     {
650
651       /*  If we have found an entry with a matching inode and dev number
652           then find the inode number for the directory in the cached string.
653           If this agrees with that returned by the stat for the current
654           directory then all is o.k. (but make sure it is a directory all
655           the same...) */
656
657       if (st.st_ino == ino_list[i].inode &&
658           st.st_dev == ino_list[i].dev)
659       {
660         if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
661         {
662           if (st.st_ino == st2.st_ino &&
663               st.st_dev == st2.st_dev &&
664               (st2.st_mode & S_IFMT) == S_IFDIR)
665           {
666             pstrcpy (path, ino_list[i].dos_path);
667
668             /* promote it for future use */
669             array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
670             return (path);
671           }
672           else
673           {
674             /*  If the inode is different then something's changed,
675                 scrub the entry and start from scratch. */
676             ino_list[i].valid = False;
677           }
678         }
679       }
680     }
681
682
683   /*  We don't have the information to hand so rely on traditional methods.
684       The very slow getcwd, which spawns a process on some systems, or the
685       not quite so bad getwd. */
686
687   if (!vfs_getwd(conn,s))
688   {
689     DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
690     return (NULL);
691   }
692
693   pstrcpy(path,s);
694
695   DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
696
697   /* add it to the cache */
698   i = MAX_GETWDCACHE - 1;
699   string_set(&ino_list[i].dos_path,s);
700   ino_list[i].dev = st.st_dev;
701   ino_list[i].inode = st.st_ino;
702   ino_list[i].valid = True;
703
704   /* put it at the top of the list */
705   array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
706
707   return (path);
708 }
709
710 /*******************************************************************
711  Reduce a file name, removing .. elements and checking that 
712  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
713  on the system that has the referenced file system.
714  Widelinks are allowed if widelinks is true.
715 ********************************************************************/
716
717 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
718 {
719 #ifndef REDUCE_PATHS
720   return True;
721 #else
722   pstring dir2;
723   pstring wd;
724   pstring base_name;
725   pstring newname;
726   char *p=NULL;
727   BOOL relative = (*s != '/');
728
729   *dir2 = *wd = *base_name = *newname = 0;
730
731   if (widelinks)
732   {
733     unix_clean_name(s);
734     /* can't have a leading .. */
735     if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
736     {
737       DEBUG(3,("Illegal file name? (%s)\n",s));
738       return(False);
739     }
740
741     if (strlen(s) == 0)
742       pstrcpy(s,"./");
743
744     return(True);
745   }
746   
747   DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
748
749   /* remove any double slashes */
750   all_string_sub(s,"//","/",0);
751
752   pstrcpy(base_name,s);
753   p = strrchr(base_name,'/');
754
755   if (!p)
756     return(True);
757
758   if (!vfs_GetWd(conn,wd))
759   {
760     DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
761     return(False);
762   }
763
764   if (vfs_ChDir(conn,dir) != 0)
765   {
766     DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
767     return(False);
768   }
769
770   if (!vfs_GetWd(conn,dir2))
771   {
772     DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
773     vfs_ChDir(conn,wd);
774     return(False);
775   }
776
777   if (p && (p != base_name))
778   {
779     *p = 0;
780     if (strcmp(p+1,".")==0)
781       p[1]=0;
782     if (strcmp(p+1,"..")==0)
783       *p = '/';
784   }
785
786   if (vfs_ChDir(conn,base_name) != 0)
787   {
788     vfs_ChDir(conn,wd);
789     DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
790     return(False);
791   }
792
793   if (!vfs_GetWd(conn,newname))
794   {
795     vfs_ChDir(conn,wd);
796     DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
797     return(False);
798   }
799
800   if (p && (p != base_name))
801   {
802     pstrcat(newname,"/");
803     pstrcat(newname,p+1);
804   }
805
806   {
807     size_t l = strlen(dir2);    
808     if (dir2[l-1] == '/')
809       l--;
810
811     if (strncmp(newname,dir2,l) != 0)
812     {
813       vfs_ChDir(conn,wd);
814       DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
815       return(False);
816     }
817
818     if (relative)
819     {
820       if (newname[l] == '/')
821         pstrcpy(s,newname + l + 1);
822       else
823         pstrcpy(s,newname+l);
824     }
825     else
826       pstrcpy(s,newname);
827   }
828
829   vfs_ChDir(conn,wd);
830
831   if (strlen(s) == 0)
832     pstrcpy(s,"./");
833
834   DEBUG(3,("reduced to %s\n",s));
835   return(True);
836 #endif
837 }