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