2 Unix SMB/Netbios implementation.
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.
28 #define DBGC_CLASS DBGC_VFS
30 struct vfs_init_function_entry {
32 vfs_op_tuple *ops, *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *);
33 struct vfs_init_function_entry *prev, *next;
36 static struct vfs_init_function_entry *backends = NULL;
38 /* Some structures to help us initialise the vfs operations table */
46 Opaque (final) vfs operations. This is a combination of first-met opaque vfs operations
47 across all currently processed modules. */
49 static vfs_op_tuple vfs_opaque_ops[SMB_VFS_OP_LAST];
51 /* Default vfs hooks. WARNING: The order of these initialisers is
52 very important. They must be in the same order as defined in
53 vfs.h. Change at your own peril. */
55 static struct vfs_ops default_vfs_ops = {
59 vfswrap_dummy_connect,
60 vfswrap_dummy_disconnect,
63 /* Directory operations */
105 /* POSIX ACL operations. */
106 #if defined(HAVE_NO_ACLS)
113 vfswrap_sys_acl_get_entry,
114 vfswrap_sys_acl_get_tag_type,
115 vfswrap_sys_acl_get_permset,
116 vfswrap_sys_acl_get_qualifier,
117 vfswrap_sys_acl_get_file,
118 vfswrap_sys_acl_get_fd,
119 vfswrap_sys_acl_clear_perms,
120 vfswrap_sys_acl_add_perm,
121 vfswrap_sys_acl_to_text,
122 vfswrap_sys_acl_init,
123 vfswrap_sys_acl_create_entry,
124 vfswrap_sys_acl_set_tag_type,
125 vfswrap_sys_acl_set_qualifier,
126 vfswrap_sys_acl_set_permset,
127 vfswrap_sys_acl_valid,
128 vfswrap_sys_acl_set_file,
129 vfswrap_sys_acl_set_fd,
130 vfswrap_sys_acl_delete_def_file,
131 vfswrap_sys_acl_get_perm,
132 vfswrap_sys_acl_free_text,
133 vfswrap_sys_acl_free_acl,
134 vfswrap_sys_acl_free_qualifier
137 /****************************************************************************
138 maintain the list of available backends
139 ****************************************************************************/
141 struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
143 struct vfs_init_function_entry *entry = backends;
146 if (strequal(entry->name, name)) return entry;
153 BOOL smb_register_vfs(const char *name, vfs_op_tuple *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *), int version)
155 struct vfs_init_function_entry *entry = backends;
157 if ((version < SMB_VFS_INTERFACE_CASCADED)) {
158 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
159 version, SMB_VFS_INTERFACE_VERSION ));
163 if ((version < SMB_VFS_INTERFACE_VERSION)) {
164 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
165 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
166 version, SMB_VFS_INTERFACE_VERSION, version ));
171 if (strequal(entry->name, name)) {
172 DEBUG(0,("VFS module %s already loaded!\n", name));
178 entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
179 entry->name = smb_xstrdup(name);
182 DLIST_ADD(backends, entry);
183 DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
187 /****************************************************************************
188 initialise default vfs hooks
189 ****************************************************************************/
191 static void vfs_init_default(connection_struct *conn)
193 DEBUG(3, ("Initialising default vfs hooks\n"));
195 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
196 conn->vfs_private = NULL;
199 /***************************************************************************
200 Function to load old VFS modules. Should go away after a while.
201 **************************************************************************/
203 static BOOL vfs_load_old_plugin(connection_struct *conn, const char *vfs_object)
205 int vfs_version = -1;
206 vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
207 /* Open object file */
209 if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
210 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
214 /* Get handle on vfs_init() symbol */
216 init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
218 if (init_fptr == NULL) {
219 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
220 sys_dlclose(conn->vfs_private->handle);
224 /* Initialise vfs_ops structure */
225 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
226 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
227 sys_dlclose(conn->vfs_private->handle);
231 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
232 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
233 vfs_version, SMB_VFS_INTERFACE_VERSION ));
234 sys_dlclose(conn->vfs_private->handle);
238 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
239 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
240 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
241 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
242 sys_dlclose(conn->vfs_private->handle);
251 /****************************************************************************
252 initialise custom vfs hooks
253 ****************************************************************************/
255 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
259 struct vfs_init_function_entry *entry;
261 DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
263 if(!backends) static_init_vfs;
265 /* First, try to load the module with the new module system */
266 if((entry = vfs_find_backend_entry(vfs_object)) ||
267 (smb_probe_module("vfs", vfs_object) &&
268 (entry = vfs_find_backend_entry(vfs_object)))) {
270 DEBUG(0,("Successfully loaded %s with the new modules system\n", vfs_object));
272 if ((ops = entry->init(&conn->vfs_ops, conn->vfs_private)) == NULL) {
273 DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
277 /* If that doesn't work, fall back to the old system
278 * (This part should go away after a while, it's only here
279 * for backwards compatibility) */
280 DEBUG(2, ("Can't load module with new modules system, falling back to old\n"));
281 if (!vfs_load_old_plugin(conn, vfs_object)) return False;
284 for(i=0; ops[i].op != NULL; i++) {
285 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
286 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
287 /* Check whether this operation was already made opaque by different module */
288 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
289 /* No, it isn't overloaded yet. Overload. */
290 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
291 vfs_opaque_ops[ops[i].type] = ops[i];
294 /* Change current VFS disposition*/
295 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
296 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
302 /*****************************************************************
304 ******************************************************************/
306 BOOL smbd_vfs_init(connection_struct *conn)
308 const char **vfs_objects;
309 char *vfs_module, *vfs_path;
312 struct smb_vfs_handle_struct *handle;
314 /* Normal share - initialise with disk access functions */
315 vfs_init_default(conn);
316 vfs_objects = lp_vfsobj(SNUM(conn));
318 /* Override VFS functions if 'vfs object' was specified*/
322 for(i=0; i<SMB_VFS_OP_LAST; i++) {
323 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
324 vfs_opaque_ops[i].type = i;
325 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
328 vfs_path = lp_vfs_path(SNUM(conn));
330 for (j=0; vfs_objects[j]; j++) {
331 conn->vfs_private = NULL;
332 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
333 /* Loadable object file */
334 handle->handle = NULL;
335 DLIST_ADD(conn->vfs_private, handle);
338 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[j]);
340 asprintf(&vfs_module, "%s", vfs_objects[j]);
342 if (!vfs_init_custom(conn, vfs_module)) {
343 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
344 SAFE_FREE(vfs_module);
345 DLIST_REMOVE(conn->vfs_private, handle);
349 SAFE_FREE(vfs_module);
354 /*******************************************************************
355 Create vfs_ops reflecting current vfs_opaque_ops
356 *******************************************************************/
358 struct vfs_ops *smb_vfs_get_opaque_ops(void)
363 ops = smb_xmalloc(sizeof(struct vfs_ops));
365 for(i=0; i<SMB_VFS_OP_LAST; i++) {
366 ((void**)ops)[i] = vfs_opaque_ops[i].op;
371 /*******************************************************************
372 Check if directory exists.
373 ********************************************************************/
375 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
383 if (vfs_stat(conn,dname,st) != 0)
386 ret = S_ISDIR(st->st_mode);
393 /*******************************************************************
395 ********************************************************************/
397 static char *vfs_getwd(connection_struct *conn, char *path)
399 return conn->vfs_ops.getwd(conn,path);
402 /*******************************************************************
404 ********************************************************************/
406 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
409 SMB_STRUCT_STAT sbuf;
411 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
413 inherit_access_acl(conn, name, mode);
416 * Check if high bits should have been set,
417 * then (if bits are missing): add them.
418 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
420 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
421 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
422 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
427 /*******************************************************************
428 Check if an object exists in the vfs.
429 ********************************************************************/
431 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
440 if (vfs_stat(conn,fname,sbuf) == -1)
445 /*******************************************************************
446 Check if a file exists in the vfs.
447 ********************************************************************/
449 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
458 if (vfs_stat(conn,fname,sbuf) == -1)
460 return(S_ISREG(sbuf->st_mode));
463 /****************************************************************************
464 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
465 ****************************************************************************/
467 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
471 while (total < byte_count)
473 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
476 if (ret == 0) return total;
485 return (ssize_t)total;
488 /****************************************************************************
489 Write data to a fd on the vfs.
490 ****************************************************************************/
492 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
498 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
507 return (ssize_t)total;
510 /****************************************************************************
511 An allocate file space call using the vfs interface.
512 Allocates space for a file from a filedescriptor.
513 Returns 0 on success, -1 on failure.
514 ****************************************************************************/
516 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
520 connection_struct *conn = fsp->conn;
521 struct vfs_ops *vfs_ops = &conn->vfs_ops;
522 SMB_BIG_UINT space_avail;
523 SMB_BIG_UINT bsize,dfree,dsize;
525 release_level_2_oplocks_on_change(fsp);
528 * Actually try and commit the space on disk....
531 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
533 if (((SMB_OFF_T)len) < 0) {
534 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
538 ret = vfs_fstat(fsp,fsp->fd,&st);
542 if (len == (SMB_BIG_UINT)st.st_size)
545 if (len < (SMB_BIG_UINT)st.st_size) {
546 /* Shrink - use ftruncate. */
548 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
549 fsp->fsp_name, (double)st.st_size ));
551 flush_write_cache(fsp, SIZECHANGE_FLUSH);
552 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
553 set_filelen_write_cache(fsp, len);
558 /* Grow - we need to test if we have enough space. */
560 if (!lp_strict_allocate(SNUM(fsp->conn)))
564 len /= 1024; /* Len is now number of 1k blocks needed. */
565 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
567 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
568 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
570 if (len > space_avail) {
578 /****************************************************************************
579 A vfs set_filelen call.
580 set the length of a file from a filedescriptor.
581 Returns 0 on success, -1 on failure.
582 ****************************************************************************/
584 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
588 release_level_2_oplocks_on_change(fsp);
589 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
590 flush_write_cache(fsp, SIZECHANGE_FLUSH);
591 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
592 set_filelen_write_cache(fsp, len);
597 /****************************************************************************
598 Transfer some data (n bytes) between two file_struct's.
599 ****************************************************************************/
601 static files_struct *in_fsp;
602 static files_struct *out_fsp;
604 static ssize_t read_fn(int fd, void *buf, size_t len)
606 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
609 static ssize_t write_fn(int fd, const void *buf, size_t len)
611 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
614 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
619 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
622 /*******************************************************************
623 A vfs_readdir wrapper which just returns the file name.
624 ********************************************************************/
626 char *vfs_readdirname(connection_struct *conn, void *p)
634 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
645 #ifdef HAVE_BROKEN_READDIR
646 /* using /usr/ucb/cc is BAD */
653 /* VFS options not quite working yet */
657 /***************************************************************************
658 handle the interpretation of the vfs option parameter
659 *************************************************************************/
660 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
662 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
665 /* Create new vfs option */
667 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
668 if (new_option == NULL) {
672 ZERO_STRUCTP(new_option);
674 /* Get name and value */
676 new_option->name = strtok(pszParmValue, "=");
678 if (new_option->name == NULL) {
682 while(isspace(*new_option->name)) {
686 for (i = strlen(new_option->name); i > 0; i--) {
687 if (!isspace(new_option->name[i - 1])) break;
690 new_option->name[i] = '\0';
691 new_option->name = strdup(new_option->name);
693 new_option->value = strtok(NULL, "=");
695 if (new_option->value != NULL) {
697 while(isspace(*new_option->value)) {
701 for (i = strlen(new_option->value); i > 0; i--) {
702 if (!isspace(new_option->value[i - 1])) break;
705 new_option->value[i] = '\0';
706 new_option->value = strdup(new_option->value);
711 DLIST_ADD(*options, new_option);
719 /*******************************************************************
720 A wrapper for vfs_chdir().
721 ********************************************************************/
723 int vfs_ChDir(connection_struct *conn, const char *path)
726 static pstring LastDir="";
728 if (strcsequal(path,"."))
731 if (*path == '/' && strcsequal(LastDir,path))
734 DEBUG(3,("vfs_ChDir to %s\n",path));
736 res = vfs_chdir(conn,path);
738 pstrcpy(LastDir,path);
742 /* number of list structures for a caching GetWd function. */
743 #define MAX_GETWDCACHE (50)
746 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
747 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
748 char *dos_path; /* The pathname in DOS format. */
750 } ino_list[MAX_GETWDCACHE];
752 extern BOOL use_getwd_cache;
754 /****************************************************************************
755 Prompte a ptr (to make it recently used)
756 ****************************************************************************/
758 static void array_promote(char *array,int elsize,int element)
764 p = (char *)malloc(elsize);
767 DEBUG(5,("array_promote: malloc fail\n"));
771 memcpy(p,array + element * elsize, elsize);
772 memmove(array + elsize,array,elsize*element);
773 memcpy(array,p,elsize);
777 /*******************************************************************
778 Return the absolute current directory path - given a UNIX pathname.
779 Note that this path is returned in DOS format, not UNIX
780 format. Note this can be called with conn == NULL.
781 ********************************************************************/
783 char *vfs_GetWd(connection_struct *conn, char *path)
786 static BOOL getwd_cache_init = False;
787 SMB_STRUCT_STAT st, st2;
792 if (!use_getwd_cache)
793 return(vfs_getwd(conn,path));
796 if (!getwd_cache_init) {
797 getwd_cache_init = True;
798 for (i=0;i<MAX_GETWDCACHE;i++) {
799 string_set(&ino_list[i].dos_path,"");
800 ino_list[i].valid = False;
804 /* Get the inode of the current directory, if this doesn't work we're
807 if (vfs_stat(conn, ".",&st) == -1) {
808 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
809 return(vfs_getwd(conn,path));
813 for (i=0; i<MAX_GETWDCACHE; i++) {
814 if (ino_list[i].valid) {
816 /* If we have found an entry with a matching inode and dev number
817 then find the inode number for the directory in the cached string.
818 If this agrees with that returned by the stat for the current
819 directory then all is o.k. (but make sure it is a directory all
822 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
823 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
824 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
825 (st2.st_mode & S_IFMT) == S_IFDIR) {
826 pstrcpy (path, ino_list[i].dos_path);
828 /* promote it for future use */
829 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
832 /* If the inode is different then something's changed,
833 scrub the entry and start from scratch. */
834 ino_list[i].valid = False;
841 /* We don't have the information to hand so rely on traditional methods.
842 The very slow getcwd, which spawns a process on some systems, or the
843 not quite so bad getwd. */
845 if (!vfs_getwd(conn,s)) {
846 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
852 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
854 /* add it to the cache */
855 i = MAX_GETWDCACHE - 1;
856 string_set(&ino_list[i].dos_path,s);
857 ino_list[i].dev = st.st_dev;
858 ino_list[i].inode = st.st_ino;
859 ino_list[i].valid = True;
861 /* put it at the top of the list */
862 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
868 /* check if the file 'nmae' is a symlink, in that case check that it point to
869 a file that reside under the 'dir' tree */
871 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
880 if (!vfs_GetWd(conn, savedir)) {
881 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
885 if (vfs_ChDir(conn, dir) != 0) {
886 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
890 if (!vfs_GetWd(conn, realdir)) {
891 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
892 vfs_ChDir(conn, savedir);
896 reallen = strlen(realdir);
897 if (realdir[reallen -1] == '/') {
899 realdir[reallen] = 0;
902 if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
903 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
905 pstrcpy(cleanlink, flink);
907 pstrcpy(cleanlink, realdir);
908 pstrcat(cleanlink, "/");
909 pstrcat(cleanlink, flink);
911 unix_clean_name(cleanlink);
913 if (strncmp(cleanlink, realdir, reallen) != 0) {
914 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
919 vfs_ChDir(conn, savedir);
924 /*******************************************************************
925 Reduce a file name, removing .. elements and checking that
926 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
927 on the system that has the referenced file system.
928 Widelinks are allowed if widelinks is true.
929 ********************************************************************/
931 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
941 BOOL relative = (*s != '/');
943 *dir2 = *wd = *base_name = *newname = 0;
947 /* can't have a leading .. */
948 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
949 DEBUG(3,("Illegal file name? (%s)\n",s));
959 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
961 /* remove any double slashes */
962 all_string_sub(s,"//","/",0);
964 pstrcpy(base_name,s);
965 p = strrchr_m(base_name,'/');
968 return readlink_check(conn, dir, s);
970 if (!vfs_GetWd(conn,wd)) {
971 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
975 if (vfs_ChDir(conn,dir) != 0) {
976 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
980 if (!vfs_GetWd(conn,dir2)) {
981 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
986 if (p && (p != base_name)) {
988 if (strcmp(p+1,".")==0)
990 if (strcmp(p+1,"..")==0)
994 if (vfs_ChDir(conn,base_name) != 0) {
996 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
1000 if (!vfs_GetWd(conn,newname)) {
1002 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
1006 if (p && (p != base_name)) {
1007 pstrcat(newname,"/");
1008 pstrcat(newname,p+1);
1012 size_t l = strlen(dir2);
1013 if (dir2[l-1] == '/')
1016 if (strncmp(newname,dir2,l) != 0) {
1018 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
1022 if (!readlink_check(conn, dir, newname)) {
1023 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
1028 if (newname[l] == '/')
1029 pstrcpy(s,newname + l + 1);
1031 pstrcpy(s,newname+l);
1041 DEBUG(3,("reduced to %s\n",s));