make proto
[Samba/gbeck.git] / source / smbd / vfs.c
blobf5bd057ca6342794081f0411b6e154662bbaf18d
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 VFS initialisation and support functions
5 Copyright (C) Tim Potter 1999
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 extern int DEBUGLEVEL;
26 /* Some structures to help us initialise the vfs operations table */
28 struct vfs_syminfo {
29 char *name;
30 void *fptr;
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. */
37 struct vfs_ops default_vfs_ops = {
39 /* Disk operations */
41 vfswrap_dummy_connect,
42 vfswrap_dummy_disconnect,
43 vfswrap_disk_free,
45 /* Directory operations */
47 vfswrap_opendir,
48 vfswrap_readdir,
49 vfswrap_mkdir,
50 vfswrap_rmdir,
51 vfswrap_closedir,
53 /* File operations */
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,
78 #if defined(HAVE_NO_ACLS)
79 NULL,
80 NULL
81 #else
82 vfswrap_chmod_acl,
83 vfswrap_fchmod_acl
84 #endif
87 /****************************************************************************
88 initialise default vfs hooks
89 ****************************************************************************/
90 int vfs_init_default(connection_struct *conn)
92 DEBUG(3, ("Initialising default vfs hooks\n"));
94 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
95 return True;
98 /****************************************************************************
99 initialise custom vfs hooks
100 ****************************************************************************/
102 #ifdef HAVE_LIBDL
103 BOOL vfs_init_custom(connection_struct *conn)
105 int vfs_version = -1;
106 struct vfs_ops *ops, *(*init_fptr)(int *);
108 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
109 lp_vfsobj(SNUM(conn))));
111 /* Open object file */
113 if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) {
114 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), dlerror()));
115 return False;
118 /* Get handle on vfs_init() symbol */
120 init_fptr = (struct vfs_ops *(*)(int *))sys_dlsym(conn->dl_handle, "vfs_init");
122 if (init_fptr == NULL) {
123 DEBUG(0, ("No vfs_init() symbol found in %s\n",
124 lp_vfsobj(SNUM(conn))));
125 return False;
128 /* Initialise vfs_ops structure */
130 if ((ops = init_fptr(&vfs_version)) == NULL) {
131 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
132 return False;
135 if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
136 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
137 vfs_version, SMB_VFS_INTERFACE_VERSION ));
138 return False;
141 /* Fill in unused operations with default (disk based) ones.
142 There's probably a neater way to do this then a whole bunch of
143 if statements. */
145 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
147 if (conn->vfs_ops.connect == NULL) {
148 conn->vfs_ops.connect = default_vfs_ops.connect;
151 if (conn->vfs_ops.disconnect == NULL) {
152 conn->vfs_ops.disconnect = default_vfs_ops.disconnect;
155 if (conn->vfs_ops.disk_free == NULL) {
156 conn->vfs_ops.disk_free = default_vfs_ops.disk_free;
159 if (conn->vfs_ops.opendir == NULL) {
160 conn->vfs_ops.opendir = default_vfs_ops.opendir;
163 if (conn->vfs_ops.readdir == NULL) {
164 conn->vfs_ops.readdir = default_vfs_ops.readdir;
167 if (conn->vfs_ops.mkdir == NULL) {
168 conn->vfs_ops.mkdir = default_vfs_ops.mkdir;
171 if (conn->vfs_ops.rmdir == NULL) {
172 conn->vfs_ops.rmdir = default_vfs_ops.rmdir;
175 if (conn->vfs_ops.closedir == NULL) {
176 conn->vfs_ops.closedir = default_vfs_ops.closedir;
179 if (conn->vfs_ops.open == NULL) {
180 conn->vfs_ops.open = default_vfs_ops.open;
183 if (conn->vfs_ops.close == NULL) {
184 conn->vfs_ops.close = default_vfs_ops.close;
187 if (conn->vfs_ops.read == NULL) {
188 conn->vfs_ops.read = default_vfs_ops.read;
191 if (conn->vfs_ops.write == NULL) {
192 conn->vfs_ops.write = default_vfs_ops.write;
195 if (conn->vfs_ops.lseek == NULL) {
196 conn->vfs_ops.lseek = default_vfs_ops.lseek;
199 if (conn->vfs_ops.rename == NULL) {
200 conn->vfs_ops.rename = default_vfs_ops.rename;
203 if (conn->vfs_ops.fsync == NULL) {
204 conn->vfs_ops.fsync = default_vfs_ops.fsync;
207 if (conn->vfs_ops.stat == NULL) {
208 conn->vfs_ops.stat = default_vfs_ops.stat;
211 if (conn->vfs_ops.fstat == NULL) {
212 conn->vfs_ops.fstat = default_vfs_ops.fstat;
215 if (conn->vfs_ops.lstat == NULL) {
216 conn->vfs_ops.lstat = default_vfs_ops.lstat;
219 if (conn->vfs_ops.unlink == NULL) {
220 conn->vfs_ops.unlink = default_vfs_ops.unlink;
223 if (conn->vfs_ops.chmod == NULL) {
224 conn->vfs_ops.chmod = default_vfs_ops.chmod;
227 if (conn->vfs_ops.chown == NULL) {
228 conn->vfs_ops.chown = default_vfs_ops.chown;
231 if (conn->vfs_ops.chdir == NULL) {
232 conn->vfs_ops.chdir = default_vfs_ops.chdir;
235 if (conn->vfs_ops.getwd == NULL) {
236 conn->vfs_ops.getwd = default_vfs_ops.getwd;
239 if (conn->vfs_ops.utime == NULL) {
240 conn->vfs_ops.utime = default_vfs_ops.utime;
243 if (conn->vfs_ops.ftruncate == NULL) {
244 conn->vfs_ops.ftruncate = default_vfs_ops.ftruncate;
247 if (conn->vfs_ops.lock == NULL) {
248 conn->vfs_ops.lock = default_vfs_ops.lock;
251 if (conn->vfs_ops.fget_nt_acl == NULL) {
252 conn->vfs_ops.fget_nt_acl = default_vfs_ops.fget_nt_acl;
255 if (conn->vfs_ops.get_nt_acl == NULL) {
256 conn->vfs_ops.get_nt_acl = default_vfs_ops.get_nt_acl;
259 if (conn->vfs_ops.fset_nt_acl == NULL) {
260 conn->vfs_ops.fset_nt_acl = default_vfs_ops.fset_nt_acl;
263 if (conn->vfs_ops.set_nt_acl == NULL) {
264 conn->vfs_ops.set_nt_acl = default_vfs_ops.set_nt_acl;
267 if (conn->vfs_ops.chmod_acl == NULL) {
268 conn->vfs_ops.chmod_acl = default_vfs_ops.chmod_acl;
271 if (conn->vfs_ops.fchmod_acl == NULL) {
272 conn->vfs_ops.fchmod_acl = default_vfs_ops.fchmod_acl;
274 return True;
276 #endif
278 /*******************************************************************
279 Check if directory exists.
280 ********************************************************************/
282 BOOL vfs_directory_exist(connection_struct *conn, char *dname, SMB_STRUCT_STAT *st)
284 SMB_STRUCT_STAT st2;
285 BOOL ret;
287 if (!st)
288 st = &st2;
290 if (vfs_stat(conn,dname,st) != 0)
291 return(False);
293 ret = S_ISDIR(st->st_mode);
294 if(!ret)
295 errno = ENOTDIR;
297 return ret;
300 /*******************************************************************
301 vfs mkdir wrapper that calls dos_to_unix.
302 ********************************************************************/
304 int vfs_mkdir(connection_struct *conn, char *fname, mode_t mode)
306 int ret;
307 pstring name;
308 SMB_STRUCT_STAT sbuf;
310 pstrcpy(name,dos_to_unix(fname,False)); /* paranoia copy */
311 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
313 * Check if high bits should have been set,
314 * then (if bits are missing): add them.
315 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
317 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
318 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
319 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
321 return ret;
324 /*******************************************************************
325 vfs getwd wrapper that calls dos_to_unix.
326 ********************************************************************/
328 char *vfs_getwd(connection_struct *conn, char *unix_path)
330 char *wd;
331 wd = conn->vfs_ops.getwd(conn,unix_path);
332 if (wd)
333 unix_to_dos(wd, True);
334 return wd;
337 /*******************************************************************
338 Check if a vfs file exists.
339 ********************************************************************/
341 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
343 SMB_STRUCT_STAT st;
345 if (!sbuf)
346 sbuf = &st;
348 ZERO_STRUCTP(sbuf);
350 if (vfs_stat(conn,fname,sbuf) != 0)
351 return(False);
353 return(S_ISREG(sbuf->st_mode));
356 /****************************************************************************
357 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
358 ****************************************************************************/
360 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
362 size_t total=0;
364 while (total < byte_count)
366 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
367 byte_count - total);
369 if (ret == 0) return total;
370 if (ret == -1) {
371 if (errno == EINTR)
372 continue;
373 else
374 return -1;
376 total += ret;
378 return (ssize_t)total;
381 /****************************************************************************
382 Write data to a fd on the vfs.
383 ****************************************************************************/
385 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
387 size_t total=0;
388 ssize_t ret;
390 while (total < N)
392 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
394 if (ret == -1) return -1;
395 if (ret == 0) return total;
397 total += ret;
399 return (ssize_t)total;
402 /****************************************************************************
403 A vfs set_filelen call.
404 set the length of a file from a filedescriptor.
405 Returns 0 on success, -1 on failure.
406 ****************************************************************************/
408 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
410 int ret;
412 release_level_2_oplocks_on_change(fsp);
413 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1) {
414 set_filelen_write_cache(fsp, len);
417 return ret;
420 /****************************************************************************
421 Transfer some data between two file_struct's.
422 ****************************************************************************/
424 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp,
425 int out_fd, files_struct *out_fsp,
426 SMB_OFF_T n, char *header, int headlen, int align)
428 static char *buf=NULL;
429 static int size=0;
430 char *buf1,*abuf;
431 SMB_OFF_T total = 0;
433 DEBUG(4,("vfs_transfer_file n=%.0f (head=%d) called\n",(double)n,headlen));
435 /* Check we have at least somewhere to read from */
437 SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
439 if (size == 0) {
440 size = lp_readsize();
441 size = MAX(size,1024);
444 while (!buf && size>0) {
445 buf = (char *)Realloc(buf,size+8);
446 if (!buf) size /= 2;
449 if (!buf) {
450 DEBUG(0,("Can't allocate transfer buffer!\n"));
451 exit(1);
454 abuf = buf + (align%8);
456 if (header)
457 n += headlen;
459 while (n > 0)
461 int s = (int)MIN(n,(SMB_OFF_T)size);
462 int ret,ret2=0;
464 ret = 0;
466 if (header && (headlen >= MIN(s,1024))) {
467 buf1 = header;
468 s = headlen;
469 ret = headlen;
470 headlen = 0;
471 header = NULL;
472 } else {
473 buf1 = abuf;
476 if (header && headlen > 0)
478 ret = MIN(headlen,size);
479 memcpy(buf1,header,ret);
480 headlen -= ret;
481 header += ret;
482 if (headlen <= 0) header = NULL;
485 if (s > ret) {
486 ret += in_fsp ?
487 in_fsp->conn->vfs_ops.read(in_fsp,in_fsp->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
490 if (ret > 0) {
491 if (out_fsp)
492 ret2 = out_fsp->conn->vfs_ops.write(out_fsp,out_fsp->fd,buf1,ret);
493 else
494 ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
497 if (ret2 > 0) total += ret2;
498 /* if we can't write then dump excess data */
499 if (ret2 != ret)
500 vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
502 if (ret <= 0 || ret2 != ret)
503 return(total);
504 n -= ret;
506 return(total);
509 /*******************************************************************
510 A vfs_readdir wrapper which just returns the file name.
511 ********************************************************************/
513 char *vfs_readdirname(connection_struct *conn, void *p)
515 struct dirent *ptr;
516 char *dname;
518 if (!p)
519 return(NULL);
521 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
522 if (!ptr)
523 return(NULL);
525 dname = ptr->d_name;
527 #ifdef NEXT2
528 if (telldir(p) < 0)
529 return(NULL);
530 #endif
532 #ifdef HAVE_BROKEN_READDIR
533 /* using /usr/ucb/cc is BAD */
534 dname = dname - 2;
535 #endif
538 static pstring buf;
539 memcpy(buf, dname, NAMLEN(ptr)+1);
540 unix_to_dos(buf, True);
541 dname = buf;
544 return(dname);
547 /* VFS options not quite working yet */
549 #if 0
551 /***************************************************************************
552 handle the interpretation of the vfs option parameter
553 *************************************************************************/
554 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
556 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
557 int i;
559 /* Create new vfs option */
561 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
562 if (new_option == NULL) {
563 return False;
566 ZERO_STRUCTP(new_option);
568 /* Get name and value */
570 new_option->name = strtok(pszParmValue, "=");
572 if (new_option->name == NULL) {
573 return False;
576 while(isspace(*new_option->name)) {
577 new_option->name++;
580 for (i = strlen(new_option->name); i > 0; i--) {
581 if (!isspace(new_option->name[i - 1])) break;
584 new_option->name[i] = '\0';
585 new_option->name = strdup(new_option->name);
587 new_option->value = strtok(NULL, "=");
589 if (new_option->value != NULL) {
591 while(isspace(*new_option->value)) {
592 new_option->value++;
595 for (i = strlen(new_option->value); i > 0; i--) {
596 if (!isspace(new_option->value[i - 1])) break;
599 new_option->value[i] = '\0';
600 new_option->value = strdup(new_option->value);
603 /* Add to list */
605 DLIST_ADD(*options, new_option);
607 return True;
610 #endif
613 /*******************************************************************
614 A wrapper for vfs_chdir().
615 ********************************************************************/
617 int vfs_ChDir(connection_struct *conn, char *path)
619 int res;
620 static pstring LastDir="";
622 if (strcsequal(path,"."))
623 return(0);
625 if (*path == '/' && strcsequal(LastDir,path))
626 return(0);
628 DEBUG(3,("vfs_ChDir to %s\n",path));
630 res = vfs_chdir(conn,path);
631 if (!res)
632 pstrcpy(LastDir,path);
633 return(res);
636 /* number of list structures for a caching GetWd function. */
637 #define MAX_GETWDCACHE (50)
639 struct
641 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
642 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
643 char *dos_path; /* The pathname in DOS format. */
644 BOOL valid;
645 } ino_list[MAX_GETWDCACHE];
647 extern BOOL use_getwd_cache;
649 /****************************************************************************
650 Prompte a ptr (to make it recently used)
651 ****************************************************************************/
653 static void array_promote(char *array,int elsize,int element)
655 char *p;
656 if (element == 0)
657 return;
659 p = (char *)malloc(elsize);
661 if (!p) {
662 DEBUG(5,("array_promote: malloc fail\n"));
663 return;
666 memcpy(p,array + element * elsize, elsize);
667 memmove(array + elsize,array,elsize*element);
668 memcpy(array,p,elsize);
669 free(p);
672 /*******************************************************************
673 Return the absolute current directory path - given a UNIX pathname.
674 Note that this path is returned in DOS format, not UNIX
675 format. Note this can be called with conn == NULL.
676 ********************************************************************/
678 char *vfs_GetWd(connection_struct *conn, char *path)
680 pstring s;
681 static BOOL getwd_cache_init = False;
682 SMB_STRUCT_STAT st, st2;
683 int i;
685 *s = 0;
687 if (!use_getwd_cache)
688 return(vfs_getwd(conn,path));
690 /* init the cache */
691 if (!getwd_cache_init)
693 getwd_cache_init = True;
694 for (i=0;i<MAX_GETWDCACHE;i++)
696 string_set(&ino_list[i].dos_path,"");
697 ino_list[i].valid = False;
701 /* Get the inode of the current directory, if this doesn't work we're
702 in trouble :-) */
704 if (vfs_stat(conn, ".",&st) == -1)
706 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
707 return(vfs_getwd(conn,path));
711 for (i=0; i<MAX_GETWDCACHE; i++)
712 if (ino_list[i].valid)
715 /* If we have found an entry with a matching inode and dev number
716 then find the inode number for the directory in the cached string.
717 If this agrees with that returned by the stat for the current
718 directory then all is o.k. (but make sure it is a directory all
719 the same...) */
721 if (st.st_ino == ino_list[i].inode &&
722 st.st_dev == ino_list[i].dev)
724 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
726 if (st.st_ino == st2.st_ino &&
727 st.st_dev == st2.st_dev &&
728 (st2.st_mode & S_IFMT) == S_IFDIR)
730 pstrcpy (path, ino_list[i].dos_path);
732 /* promote it for future use */
733 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
734 return (path);
736 else
738 /* If the inode is different then something's changed,
739 scrub the entry and start from scratch. */
740 ino_list[i].valid = False;
747 /* We don't have the information to hand so rely on traditional methods.
748 The very slow getcwd, which spawns a process on some systems, or the
749 not quite so bad getwd. */
751 if (!vfs_getwd(conn,s))
753 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
754 return (NULL);
757 pstrcpy(path,s);
759 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
761 /* add it to the cache */
762 i = MAX_GETWDCACHE - 1;
763 string_set(&ino_list[i].dos_path,s);
764 ino_list[i].dev = st.st_dev;
765 ino_list[i].inode = st.st_ino;
766 ino_list[i].valid = True;
768 /* put it at the top of the list */
769 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
771 return (path);
774 /*******************************************************************
775 Reduce a file name, removing .. elements and checking that
776 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
777 on the system that has the referenced file system.
778 Widelinks are allowed if widelinks is true.
779 ********************************************************************/
781 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
783 #ifndef REDUCE_PATHS
784 return True;
785 #else
786 pstring dir2;
787 pstring wd;
788 pstring base_name;
789 pstring newname;
790 char *p=NULL;
791 BOOL relative = (*s != '/');
793 *dir2 = *wd = *base_name = *newname = 0;
795 if (widelinks)
797 unix_clean_name(s);
798 /* can't have a leading .. */
799 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
801 DEBUG(3,("Illegal file name? (%s)\n",s));
802 return(False);
805 if (strlen(s) == 0)
806 pstrcpy(s,"./");
808 return(True);
811 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
813 /* remove any double slashes */
814 all_string_sub(s,"//","/",0);
816 pstrcpy(base_name,s);
817 p = strrchr(base_name,'/');
819 if (!p)
820 return(True);
822 if (!vfs_GetWd(conn,wd))
824 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
825 return(False);
828 if (vfs_ChDir(conn,dir) != 0)
830 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
831 return(False);
834 if (!vfs_GetWd(conn,dir2))
836 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
837 vfs_ChDir(conn,wd);
838 return(False);
841 if (p && (p != base_name))
843 *p = 0;
844 if (strcmp(p+1,".")==0)
845 p[1]=0;
846 if (strcmp(p+1,"..")==0)
847 *p = '/';
850 if (vfs_ChDir(conn,base_name) != 0)
852 vfs_ChDir(conn,wd);
853 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
854 return(False);
857 if (!vfs_GetWd(conn,newname))
859 vfs_ChDir(conn,wd);
860 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
861 return(False);
864 if (p && (p != base_name))
866 pstrcat(newname,"/");
867 pstrcat(newname,p+1);
871 size_t l = strlen(dir2);
872 if (dir2[l-1] == '/')
873 l--;
875 if (strncmp(newname,dir2,l) != 0)
877 vfs_ChDir(conn,wd);
878 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
879 return(False);
882 if (relative)
884 if (newname[l] == '/')
885 pstrcpy(s,newname + l + 1);
886 else
887 pstrcpy(s,newname+l);
889 else
890 pstrcpy(s,newname);
893 vfs_ChDir(conn,wd);
895 if (strlen(s) == 0)
896 pstrcpy(s,"./");
898 DEBUG(3,("reduced to %s\n",s));
899 return(True);
900 #endif