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