Herb's warning fixes. Also the POSIX locking fix.
[samba.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         if (vfs_stat(conn,fname,sbuf) != 0) 
328                 return(False);
329
330         return(S_ISREG(sbuf->st_mode));
331 }
332
333 /****************************************************************************
334  Write data to a fd on the vfs.
335 ****************************************************************************/
336
337 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
338 {
339   size_t total=0;
340   ssize_t ret;
341
342   while (total < N)
343   {
344     ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
345
346     if (ret == -1) return -1;
347     if (ret == 0) return total;
348
349     total += ret;
350   }
351   return (ssize_t)total;
352 }
353
354 /****************************************************************************
355  Transfer some data between two file_struct's.
356 ****************************************************************************/
357
358 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp, 
359                             int out_fd, files_struct *out_fsp,
360                             SMB_OFF_T n, char *header, int headlen, int align)
361 {
362   static char *buf=NULL;  
363   static int size=0;
364   char *buf1,*abuf;
365   SMB_OFF_T total = 0;
366
367   DEBUG(4,("vfs_transfer_file n=%.0f  (head=%d) called\n",(double)n,headlen));
368
369   /* Check we have at least somewhere to read from */
370
371   SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
372
373   if (size == 0) {
374     size = lp_readsize();
375     size = MAX(size,1024);
376   }
377
378   while (!buf && size>0) {
379     buf = (char *)Realloc(buf,size+8);
380     if (!buf) size /= 2;
381   }
382
383   if (!buf) {
384     DEBUG(0,("Can't allocate transfer buffer!\n"));
385     exit(1);
386   }
387
388   abuf = buf + (align%8);
389
390   if (header)
391     n += headlen;
392
393   while (n > 0)
394   {
395     int s = (int)MIN(n,(SMB_OFF_T)size);
396     int ret,ret2=0;
397
398     ret = 0;
399
400     if (header && (headlen >= MIN(s,1024))) {
401       buf1 = header;
402       s = headlen;
403       ret = headlen;
404       headlen = 0;
405       header = NULL;
406     } else {
407       buf1 = abuf;
408     }
409
410     if (header && headlen > 0)
411     {
412       ret = MIN(headlen,size);
413       memcpy(buf1,header,ret);
414       headlen -= ret;
415       header += ret;
416       if (headlen <= 0) header = NULL;
417     }
418
419     if (s > ret) {
420       ret += in_fsp ? 
421           in_fsp->conn->vfs_ops.read(in_fsp,in_fsp->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
422     }
423
424     if (ret > 0) {
425                 if (out_fsp)
426                     ret2 = out_fsp->conn->vfs_ops.write(out_fsp,out_fsp->fd,buf1,ret);
427                 else
428                     ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
429     }
430
431       if (ret2 > 0) total += ret2;
432       /* if we can't write then dump excess data */
433       if (ret2 != ret)
434         vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
435
436     if (ret <= 0 || ret2 != ret)
437       return(total);
438     n -= ret;
439   }
440   return(total);
441 }
442
443 /*******************************************************************
444  A vfs_readdir wrapper which just returns the file name.
445 ********************************************************************/
446
447 char *vfs_readdirname(connection_struct *conn, void *p)
448 {
449         struct dirent *ptr;
450         char *dname;
451
452         if (!p)
453                 return(NULL);
454   
455         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
456         if (!ptr)
457                 return(NULL);
458
459         dname = ptr->d_name;
460
461 #ifdef NEXT2
462         if (telldir(p) < 0)
463                 return(NULL);
464 #endif
465
466 #ifdef HAVE_BROKEN_READDIR
467         /* using /usr/ucb/cc is BAD */
468         dname = dname - 2;
469 #endif
470
471         {
472                 static pstring buf;
473                 memcpy(buf, dname, NAMLEN(ptr)+1);
474                 unix_to_dos(buf, True);
475                 dname = buf;
476         }
477
478         return(dname);
479 }
480
481 /* VFS options not quite working yet */
482
483 #if 0
484
485 /***************************************************************************
486   handle the interpretation of the vfs option parameter
487  *************************************************************************/
488 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
489 {
490     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
491     int i;
492     
493     /* Create new vfs option */
494
495     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
496     if (new_option == NULL) {
497         return False;
498     }
499
500     ZERO_STRUCTP(new_option);
501
502     /* Get name and value */
503     
504     new_option->name = strtok(pszParmValue, "=");
505
506     if (new_option->name == NULL) {
507         return False;
508     }
509
510     while(isspace(*new_option->name)) {
511         new_option->name++;
512     }
513
514     for (i = strlen(new_option->name); i > 0; i--) {
515         if (!isspace(new_option->name[i - 1])) break;
516     }
517
518     new_option->name[i] = '\0';
519     new_option->name = strdup(new_option->name);
520
521     new_option->value = strtok(NULL, "=");
522
523     if (new_option->value != NULL) {
524
525         while(isspace(*new_option->value)) {
526             new_option->value++;
527         }
528         
529         for (i = strlen(new_option->value); i > 0; i--) {
530             if (!isspace(new_option->value[i - 1])) break;
531         }
532         
533         new_option->value[i] = '\0';
534         new_option->value = strdup(new_option->value);
535     }
536
537     /* Add to list */
538
539     DLIST_ADD(*options, new_option);
540
541     return True;
542 }
543
544 #endif
545
546
547 /*******************************************************************
548  A wrapper for vfs_chdir().
549 ********************************************************************/
550
551 int vfs_ChDir(connection_struct *conn, char *path)
552 {
553         int res;
554         static pstring LastDir="";
555
556         if (strcsequal(path,"."))
557                 return(0);
558
559         if (*path == '/' && strcsequal(LastDir,path))
560                 return(0);
561
562         DEBUG(3,("vfs_ChDir to %s\n",path));
563
564         res = vfs_chdir(conn,path);
565         if (!res)
566                 pstrcpy(LastDir,path);
567         return(res);
568 }
569
570 /* number of list structures for a caching GetWd function. */
571 #define MAX_GETWDCACHE (50)
572
573 struct
574 {
575   SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
576   SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
577   char *dos_path; /* The pathname in DOS format. */
578   BOOL valid;
579 } ino_list[MAX_GETWDCACHE];
580
581 extern BOOL use_getwd_cache;
582
583 /****************************************************************************
584  Prompte a ptr (to make it recently used)
585 ****************************************************************************/
586
587 static void array_promote(char *array,int elsize,int element)
588 {
589         char *p;
590         if (element == 0)
591                 return;
592
593         p = (char *)malloc(elsize);
594
595         if (!p) {
596                 DEBUG(5,("array_promote: malloc fail\n"));
597                 return;
598         }
599
600         memcpy(p,array + element * elsize, elsize);
601         memmove(array + elsize,array,elsize*element);
602         memcpy(array,p,elsize);
603         free(p);
604 }
605
606 /*******************************************************************
607  Return the absolute current directory path - given a UNIX pathname.
608  Note that this path is returned in DOS format, not UNIX
609  format. Note this can be called with conn == NULL.
610 ********************************************************************/
611
612 char *vfs_GetWd(connection_struct *conn, char *path)
613 {
614   pstring s;
615   static BOOL getwd_cache_init = False;
616   SMB_STRUCT_STAT st, st2;
617   int i;
618
619   *s = 0;
620
621   if (!use_getwd_cache)
622     return(vfs_getwd(conn,path));
623
624   /* init the cache */
625   if (!getwd_cache_init)
626   {
627     getwd_cache_init = True;
628     for (i=0;i<MAX_GETWDCACHE;i++)
629     {
630       string_set(&ino_list[i].dos_path,"");
631       ino_list[i].valid = False;
632     }
633   }
634
635   /*  Get the inode of the current directory, if this doesn't work we're
636       in trouble :-) */
637
638   if (vfs_stat(conn, ".",&st) == -1)
639   {
640     DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
641     return(vfs_getwd(conn,path));
642   }
643
644
645   for (i=0; i<MAX_GETWDCACHE; i++)
646     if (ino_list[i].valid)
647     {
648
649       /*  If we have found an entry with a matching inode and dev number
650           then find the inode number for the directory in the cached string.
651           If this agrees with that returned by the stat for the current
652           directory then all is o.k. (but make sure it is a directory all
653           the same...) */
654
655       if (st.st_ino == ino_list[i].inode &&
656           st.st_dev == ino_list[i].dev)
657       {
658         if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
659         {
660           if (st.st_ino == st2.st_ino &&
661               st.st_dev == st2.st_dev &&
662               (st2.st_mode & S_IFMT) == S_IFDIR)
663           {
664             pstrcpy (path, ino_list[i].dos_path);
665
666             /* promote it for future use */
667             array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
668             return (path);
669           }
670           else
671           {
672             /*  If the inode is different then something's changed,
673                 scrub the entry and start from scratch. */
674             ino_list[i].valid = False;
675           }
676         }
677       }
678     }
679
680
681   /*  We don't have the information to hand so rely on traditional methods.
682       The very slow getcwd, which spawns a process on some systems, or the
683       not quite so bad getwd. */
684
685   if (!vfs_getwd(conn,s))
686   {
687     DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
688     return (NULL);
689   }
690
691   pstrcpy(path,s);
692
693   DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
694
695   /* add it to the cache */
696   i = MAX_GETWDCACHE - 1;
697   string_set(&ino_list[i].dos_path,s);
698   ino_list[i].dev = st.st_dev;
699   ino_list[i].inode = st.st_ino;
700   ino_list[i].valid = True;
701
702   /* put it at the top of the list */
703   array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
704
705   return (path);
706 }
707
708 /*******************************************************************
709  Reduce a file name, removing .. elements and checking that 
710  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
711  on the system that has the referenced file system.
712  Widelinks are allowed if widelinks is true.
713 ********************************************************************/
714
715 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
716 {
717 #ifndef REDUCE_PATHS
718   return True;
719 #else
720   pstring dir2;
721   pstring wd;
722   pstring base_name;
723   pstring newname;
724   char *p=NULL;
725   BOOL relative = (*s != '/');
726
727   *dir2 = *wd = *base_name = *newname = 0;
728
729   if (widelinks)
730   {
731     unix_clean_name(s);
732     /* can't have a leading .. */
733     if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
734     {
735       DEBUG(3,("Illegal file name? (%s)\n",s));
736       return(False);
737     }
738
739     if (strlen(s) == 0)
740       pstrcpy(s,"./");
741
742     return(True);
743   }
744   
745   DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
746
747   /* remove any double slashes */
748   all_string_sub(s,"//","/",0);
749
750   pstrcpy(base_name,s);
751   p = strrchr(base_name,'/');
752
753   if (!p)
754     return(True);
755
756   if (!vfs_GetWd(conn,wd))
757   {
758     DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
759     return(False);
760   }
761
762   if (vfs_ChDir(conn,dir) != 0)
763   {
764     DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
765     return(False);
766   }
767
768   if (!vfs_GetWd(conn,dir2))
769   {
770     DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
771     vfs_ChDir(conn,wd);
772     return(False);
773   }
774
775   if (p && (p != base_name))
776   {
777     *p = 0;
778     if (strcmp(p+1,".")==0)
779       p[1]=0;
780     if (strcmp(p+1,"..")==0)
781       *p = '/';
782   }
783
784   if (vfs_ChDir(conn,base_name) != 0)
785   {
786     vfs_ChDir(conn,wd);
787     DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
788     return(False);
789   }
790
791   if (!vfs_GetWd(conn,newname))
792   {
793     vfs_ChDir(conn,wd);
794     DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
795     return(False);
796   }
797
798   if (p && (p != base_name))
799   {
800     pstrcat(newname,"/");
801     pstrcat(newname,p+1);
802   }
803
804   {
805     size_t l = strlen(dir2);    
806     if (dir2[l-1] == '/')
807       l--;
808
809     if (strncmp(newname,dir2,l) != 0)
810     {
811       vfs_ChDir(conn,wd);
812       DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
813       return(False);
814     }
815
816     if (relative)
817     {
818       if (newname[l] == '/')
819         pstrcpy(s,newname + l + 1);
820       else
821         pstrcpy(s,newname+l);
822     }
823     else
824       pstrcpy(s,newname);
825   }
826
827   vfs_ChDir(conn,wd);
828
829   if (strlen(s) == 0)
830     pstrcpy(s,"./");
831
832   DEBUG(3,("reduced to %s\n",s));
833   return(True);
834 #endif
835 }