sync'ing up for 3.0alpha20 release
[Samba/gbeck.git] / source3 / smbd / vfs.c
bloba0a7b920b8bb47813167b40f63a63e2a95a2c463
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 VFS initialisation and support functions
5 Copyright (C) Tim Potter 1999
6 Copyright (C) Alexander Bokovoy 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 This work was sponsored by Optifacio Software Services, Inc.
25 #include "includes.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_VFS
31 /* Some structures to help us initialise the vfs operations table */
33 struct vfs_syminfo {
34 char *name;
35 void *fptr;
39 Opaque (final) vfs operations. This is a combination of first-met opaque vfs operations
40 across all currently processed modules. */
42 static vfs_op_tuple vfs_opaque_ops[SMB_VFS_OP_LAST];
44 /* Default vfs hooks. WARNING: The order of these initialisers is
45 very important. They must be in the same order as defined in
46 vfs.h. Change at your own peril. */
48 static struct vfs_ops default_vfs_ops = {
50 /* Disk operations */
52 vfswrap_dummy_connect,
53 vfswrap_dummy_disconnect,
54 vfswrap_disk_free,
56 /* Directory operations */
58 vfswrap_opendir,
59 vfswrap_readdir,
60 vfswrap_mkdir,
61 vfswrap_rmdir,
62 vfswrap_closedir,
64 /* File operations */
66 vfswrap_open,
67 vfswrap_close,
68 vfswrap_read,
69 vfswrap_write,
70 vfswrap_lseek,
71 vfswrap_sendfile,
72 vfswrap_rename,
73 vfswrap_fsync,
74 vfswrap_stat,
75 vfswrap_fstat,
76 vfswrap_lstat,
77 vfswrap_unlink,
78 vfswrap_chmod,
79 vfswrap_fchmod,
80 vfswrap_chown,
81 vfswrap_fchown,
82 vfswrap_chdir,
83 vfswrap_getwd,
84 vfswrap_utime,
85 vfswrap_ftruncate,
86 vfswrap_lock,
87 vfswrap_symlink,
88 vfswrap_readlink,
89 vfswrap_link,
90 vfswrap_mknod,
91 vfswrap_realpath,
93 vfswrap_fget_nt_acl,
94 vfswrap_get_nt_acl,
95 vfswrap_fset_nt_acl,
96 vfswrap_set_nt_acl,
98 /* POSIX ACL operations. */
99 #if defined(HAVE_NO_ACLS)
100 NULL,
101 NULL,
102 #else
103 vfswrap_chmod_acl,
104 vfswrap_fchmod_acl,
105 #endif
106 vfswrap_sys_acl_get_entry,
107 vfswrap_sys_acl_get_tag_type,
108 vfswrap_sys_acl_get_permset,
109 vfswrap_sys_acl_get_qualifier,
110 vfswrap_sys_acl_get_file,
111 vfswrap_sys_acl_get_fd,
112 vfswrap_sys_acl_clear_perms,
113 vfswrap_sys_acl_add_perm,
114 vfswrap_sys_acl_to_text,
115 vfswrap_sys_acl_init,
116 vfswrap_sys_acl_create_entry,
117 vfswrap_sys_acl_set_tag_type,
118 vfswrap_sys_acl_set_qualifier,
119 vfswrap_sys_acl_set_permset,
120 vfswrap_sys_acl_valid,
121 vfswrap_sys_acl_set_file,
122 vfswrap_sys_acl_set_fd,
123 vfswrap_sys_acl_delete_def_file,
124 vfswrap_sys_acl_get_perm,
125 vfswrap_sys_acl_free_text,
126 vfswrap_sys_acl_free_acl,
127 vfswrap_sys_acl_free_qualifier
130 /****************************************************************************
131 initialise default vfs hooks
132 ****************************************************************************/
134 static void vfs_init_default(connection_struct *conn)
136 DEBUG(3, ("Initialising default vfs hooks\n"));
138 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
139 conn->vfs_private = NULL;
142 /****************************************************************************
143 initialise custom vfs hooks
144 ****************************************************************************/
146 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
148 int vfs_version = -1;
149 vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
150 int i;
152 DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
154 /* Open object file */
156 if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
157 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
158 return False;
161 /* Get handle on vfs_init() symbol */
163 init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
165 if (init_fptr == NULL) {
166 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
167 return False;
170 /* Initialise vfs_ops structure */
172 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
173 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
174 return False;
177 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
178 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
179 vfs_version, SMB_VFS_INTERFACE_VERSION ));
180 return False;
183 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
184 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
185 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
186 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
187 return False;
190 for(i=0; ops[i].op != NULL; i++) {
191 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
192 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
193 /* Check whether this operation was already made opaque by different module */
194 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
195 /* No, it isn't overloaded yet. Overload. */
196 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
197 vfs_opaque_ops[ops[i].type] = ops[i];
200 /* Change current VFS disposition*/
201 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
202 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
205 return True;
208 /*****************************************************************
209 Generic VFS init.
210 ******************************************************************/
212 BOOL smbd_vfs_init(connection_struct *conn)
214 char **vfs_objects, *vfsobj, *vfs_module, *vfs_path;
215 int nobj, i;
216 struct smb_vfs_handle_struct *handle;
218 /* Normal share - initialise with disk access functions */
219 vfs_init_default(conn);
221 /* Override VFS functions if 'vfs object' was specified*/
222 if (*lp_vfsobj(SNUM(conn))) {
223 vfsobj = NULL;
224 for(i=0; i<SMB_VFS_OP_LAST; i++) {
225 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
226 vfs_opaque_ops[i].type = i;
227 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
229 if (string_set(&vfsobj, lp_vfsobj(SNUM(conn)))) {
230 /* Parse passed modules specification to array of modules */
231 set_first_token(vfsobj);
232 /* We are using default separators: ' \t\r\n' */
233 vfs_objects = toktocliplist(&nobj, NULL);
234 if (vfs_objects) {
235 vfs_path = lp_vfs_path(SNUM(conn));
236 conn->vfs_private = NULL;
237 for(i=nobj-1; i>=0; i--) {
238 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
239 /* Loadable object file */
240 handle->handle = NULL;
241 DLIST_ADD(conn->vfs_private, handle)
242 vfs_module = NULL;
243 if (vfs_path) {
244 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[i]);
245 } else {
246 asprintf(&vfs_module, "%s", vfs_objects[i]);
248 if (!vfs_init_custom(conn, vfs_module)) {
249 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
250 string_free(&vfsobj);
251 SAFE_FREE(vfs_module);
252 return False;
254 SAFE_FREE(vfs_module);
257 string_free(&vfsobj);
258 return True;
261 return True;
264 /*******************************************************************
265 Create vfs_ops reflecting current vfs_opaque_ops
266 *******************************************************************/
268 struct vfs_ops *smb_vfs_get_opaque_ops(void)
270 int i;
271 struct vfs_ops *ops;
273 ops = smb_xmalloc(sizeof(struct vfs_ops));
275 for(i=0; i<SMB_VFS_OP_LAST; i++) {
276 ((void**)ops)[i] = vfs_opaque_ops[i].op;
278 return ops;
281 /*******************************************************************
282 Check if directory exists.
283 ********************************************************************/
285 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
287 SMB_STRUCT_STAT st2;
288 BOOL ret;
290 if (!st)
291 st = &st2;
293 if (vfs_stat(conn,dname,st) != 0)
294 return(False);
296 ret = S_ISDIR(st->st_mode);
297 if(!ret)
298 errno = ENOTDIR;
300 return ret;
303 /*******************************************************************
304 vfs getwd wrapper
305 ********************************************************************/
307 static char *vfs_getwd(connection_struct *conn, char *path)
309 return conn->vfs_ops.getwd(conn,path);
312 /*******************************************************************
313 vfs mkdir wrapper
314 ********************************************************************/
316 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
318 int ret;
319 SMB_STRUCT_STAT sbuf;
321 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
323 * Check if high bits should have been set,
324 * then (if bits are missing): add them.
325 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
327 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
328 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
329 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
331 return ret;
334 /*******************************************************************
335 Check if an object exists in the vfs.
336 ********************************************************************/
338 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
340 SMB_STRUCT_STAT st;
342 if (!sbuf)
343 sbuf = &st;
345 ZERO_STRUCTP(sbuf);
347 if (vfs_stat(conn,fname,sbuf) == -1)
348 return(False);
349 return True;
352 /*******************************************************************
353 Check if a file exists in the vfs.
354 ********************************************************************/
356 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
358 SMB_STRUCT_STAT st;
360 if (!sbuf)
361 sbuf = &st;
363 ZERO_STRUCTP(sbuf);
365 if (vfs_stat(conn,fname,sbuf) == -1)
366 return False;
367 return(S_ISREG(sbuf->st_mode));
370 /****************************************************************************
371 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
372 ****************************************************************************/
374 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
376 size_t total=0;
378 while (total < byte_count)
380 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
381 byte_count - total);
383 if (ret == 0) return total;
384 if (ret == -1) {
385 if (errno == EINTR)
386 continue;
387 else
388 return -1;
390 total += ret;
392 return (ssize_t)total;
395 /****************************************************************************
396 Write data to a fd on the vfs.
397 ****************************************************************************/
399 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
401 size_t total=0;
402 ssize_t ret;
404 while (total < N) {
405 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
407 if (ret == -1)
408 return -1;
409 if (ret == 0)
410 return total;
412 total += ret;
414 return (ssize_t)total;
417 /****************************************************************************
418 An allocate file space call using the vfs interface.
419 Allocates space for a file from a filedescriptor.
420 Returns 0 on success, -1 on failure.
421 ****************************************************************************/
423 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
425 int ret;
426 SMB_STRUCT_STAT st;
427 connection_struct *conn = fsp->conn;
428 struct vfs_ops *vfs_ops = &conn->vfs_ops;
429 SMB_OFF_T space_avail;
430 SMB_BIG_UINT bsize,dfree,dsize;
432 release_level_2_oplocks_on_change(fsp);
435 * Actually try and commit the space on disk....
438 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
440 ret = vfs_fstat(fsp,fsp->fd,&st);
441 if (ret == -1)
442 return ret;
444 if (len == st.st_size)
445 return 0;
447 if (len < st.st_size) {
448 /* Shrink - use ftruncate. */
450 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
451 fsp->fsp_name, (double)st.st_size ));
453 flush_write_cache(fsp, SIZECHANGE_FLUSH);
454 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
455 set_filelen_write_cache(fsp, len);
457 return ret;
460 /* Grow - we need to test if we have enough space. */
462 if (!lp_strict_allocate(SNUM(fsp->conn)))
463 return 0;
465 len -= st.st_size;
466 len /= 1024; /* Len is now number of 1k blocks needed. */
467 space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
469 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
470 fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
472 if (len > space_avail) {
473 errno = ENOSPC;
474 return -1;
477 return 0;
480 /****************************************************************************
481 A vfs set_filelen call.
482 set the length of a file from a filedescriptor.
483 Returns 0 on success, -1 on failure.
484 ****************************************************************************/
486 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
488 int ret;
490 release_level_2_oplocks_on_change(fsp);
491 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
492 flush_write_cache(fsp, SIZECHANGE_FLUSH);
493 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
494 set_filelen_write_cache(fsp, len);
496 return ret;
499 /****************************************************************************
500 Transfer some data (n bytes) between two file_struct's.
501 ****************************************************************************/
503 static files_struct *in_fsp;
504 static files_struct *out_fsp;
506 static ssize_t read_fn(int fd, void *buf, size_t len)
508 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
511 static ssize_t write_fn(int fd, const void *buf, size_t len)
513 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
516 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
518 in_fsp = in;
519 out_fsp = out;
521 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
524 /*******************************************************************
525 A vfs_readdir wrapper which just returns the file name.
526 ********************************************************************/
528 char *vfs_readdirname(connection_struct *conn, void *p)
530 struct dirent *ptr;
531 char *dname;
533 if (!p)
534 return(NULL);
536 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
537 if (!ptr)
538 return(NULL);
540 dname = ptr->d_name;
542 #ifdef NEXT2
543 if (telldir(p) < 0)
544 return(NULL);
545 #endif
547 #ifdef HAVE_BROKEN_READDIR
548 /* using /usr/ucb/cc is BAD */
549 dname = dname - 2;
550 #endif
552 return(dname);
555 /* VFS options not quite working yet */
557 #if 0
559 /***************************************************************************
560 handle the interpretation of the vfs option parameter
561 *************************************************************************/
562 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
564 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
565 int i;
567 /* Create new vfs option */
569 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
570 if (new_option == NULL) {
571 return False;
574 ZERO_STRUCTP(new_option);
576 /* Get name and value */
578 new_option->name = strtok(pszParmValue, "=");
580 if (new_option->name == NULL) {
581 return False;
584 while(isspace(*new_option->name)) {
585 new_option->name++;
588 for (i = strlen(new_option->name); i > 0; i--) {
589 if (!isspace(new_option->name[i - 1])) break;
592 new_option->name[i] = '\0';
593 new_option->name = strdup(new_option->name);
595 new_option->value = strtok(NULL, "=");
597 if (new_option->value != NULL) {
599 while(isspace(*new_option->value)) {
600 new_option->value++;
603 for (i = strlen(new_option->value); i > 0; i--) {
604 if (!isspace(new_option->value[i - 1])) break;
607 new_option->value[i] = '\0';
608 new_option->value = strdup(new_option->value);
611 /* Add to list */
613 DLIST_ADD(*options, new_option);
615 return True;
618 #endif
621 /*******************************************************************
622 A wrapper for vfs_chdir().
623 ********************************************************************/
625 int vfs_ChDir(connection_struct *conn, char *path)
627 int res;
628 static pstring LastDir="";
630 if (strcsequal(path,"."))
631 return(0);
633 if (*path == '/' && strcsequal(LastDir,path))
634 return(0);
636 DEBUG(3,("vfs_ChDir to %s\n",path));
638 res = vfs_chdir(conn,path);
639 if (!res)
640 pstrcpy(LastDir,path);
641 return(res);
644 /* number of list structures for a caching GetWd function. */
645 #define MAX_GETWDCACHE (50)
647 static struct {
648 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
649 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
650 char *dos_path; /* The pathname in DOS format. */
651 BOOL valid;
652 } ino_list[MAX_GETWDCACHE];
654 extern BOOL use_getwd_cache;
656 /****************************************************************************
657 Prompte a ptr (to make it recently used)
658 ****************************************************************************/
660 static void array_promote(char *array,int elsize,int element)
662 char *p;
663 if (element == 0)
664 return;
666 p = (char *)malloc(elsize);
668 if (!p) {
669 DEBUG(5,("array_promote: malloc fail\n"));
670 return;
673 memcpy(p,array + element * elsize, elsize);
674 memmove(array + elsize,array,elsize*element);
675 memcpy(array,p,elsize);
676 SAFE_FREE(p);
679 /*******************************************************************
680 Return the absolute current directory path - given a UNIX pathname.
681 Note that this path is returned in DOS format, not UNIX
682 format. Note this can be called with conn == NULL.
683 ********************************************************************/
685 char *vfs_GetWd(connection_struct *conn, char *path)
687 pstring s;
688 static BOOL getwd_cache_init = False;
689 SMB_STRUCT_STAT st, st2;
690 int i;
692 *s = 0;
694 if (!use_getwd_cache)
695 return(vfs_getwd(conn,path));
697 /* init the cache */
698 if (!getwd_cache_init) {
699 getwd_cache_init = True;
700 for (i=0;i<MAX_GETWDCACHE;i++) {
701 string_set(&ino_list[i].dos_path,"");
702 ino_list[i].valid = False;
706 /* Get the inode of the current directory, if this doesn't work we're
707 in trouble :-) */
709 if (vfs_stat(conn, ".",&st) == -1) {
710 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
711 return(vfs_getwd(conn,path));
715 for (i=0; i<MAX_GETWDCACHE; i++) {
716 if (ino_list[i].valid) {
718 /* If we have found an entry with a matching inode and dev number
719 then find the inode number for the directory in the cached string.
720 If this agrees with that returned by the stat for the current
721 directory then all is o.k. (but make sure it is a directory all
722 the same...) */
724 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
725 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
726 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
727 (st2.st_mode & S_IFMT) == S_IFDIR) {
728 pstrcpy (path, ino_list[i].dos_path);
730 /* promote it for future use */
731 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
732 return (path);
733 } else {
734 /* If the inode is different then something's changed,
735 scrub the entry and start from scratch. */
736 ino_list[i].valid = False;
743 /* We don't have the information to hand so rely on traditional methods.
744 The very slow getcwd, which spawns a process on some systems, or the
745 not quite so bad getwd. */
747 if (!vfs_getwd(conn,s)) {
748 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
749 return (NULL);
752 pstrcpy(path,s);
754 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
756 /* add it to the cache */
757 i = MAX_GETWDCACHE - 1;
758 string_set(&ino_list[i].dos_path,s);
759 ino_list[i].dev = st.st_dev;
760 ino_list[i].inode = st.st_ino;
761 ino_list[i].valid = True;
763 /* put it at the top of the list */
764 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
766 return (path);
769 /*******************************************************************
770 Reduce a file name, removing .. elements and checking that
771 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
772 on the system that has the referenced file system.
773 Widelinks are allowed if widelinks is true.
774 ********************************************************************/
776 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
778 #ifndef REDUCE_PATHS
779 return True;
780 #else
781 pstring dir2;
782 pstring wd;
783 pstring base_name;
784 pstring newname;
785 char *p=NULL;
786 BOOL relative = (*s != '/');
788 *dir2 = *wd = *base_name = *newname = 0;
790 if (widelinks) {
791 unix_clean_name(s);
792 /* can't have a leading .. */
793 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
794 DEBUG(3,("Illegal file name? (%s)\n",s));
795 return(False);
798 if (strlen(s) == 0)
799 pstrcpy(s,"./");
801 return(True);
804 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
806 /* remove any double slashes */
807 all_string_sub(s,"//","/",0);
809 pstrcpy(base_name,s);
810 p = strrchr_m(base_name,'/');
812 if (!p)
813 return(True);
815 if (!vfs_GetWd(conn,wd)) {
816 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
817 return(False);
820 if (vfs_ChDir(conn,dir) != 0) {
821 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
822 return(False);
825 if (!vfs_GetWd(conn,dir2)) {
826 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
827 vfs_ChDir(conn,wd);
828 return(False);
831 if (p && (p != base_name)) {
832 *p = 0;
833 if (strcmp(p+1,".")==0)
834 p[1]=0;
835 if (strcmp(p+1,"..")==0)
836 *p = '/';
839 if (vfs_ChDir(conn,base_name) != 0) {
840 vfs_ChDir(conn,wd);
841 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
842 return(False);
845 if (!vfs_GetWd(conn,newname)) {
846 vfs_ChDir(conn,wd);
847 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
848 return(False);
851 if (p && (p != base_name)) {
852 pstrcat(newname,"/");
853 pstrcat(newname,p+1);
857 size_t l = strlen(dir2);
858 if (dir2[l-1] == '/')
859 l--;
861 if (strncmp(newname,dir2,l) != 0) {
862 vfs_ChDir(conn,wd);
863 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
864 return(False);
867 if (relative) {
868 if (newname[l] == '/')
869 pstrcpy(s,newname + l + 1);
870 else
871 pstrcpy(s,newname+l);
872 } else
873 pstrcpy(s,newname);
876 vfs_ChDir(conn,wd);
878 if (strlen(s) == 0)
879 pstrcpy(s,"./");
881 DEBUG(3,("reduced to %s\n",s));
882 return(True);
883 #endif