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 module_path_get_name(name, stripped);
149 if (strequal(entry->name, stripped)) return entry;
156 BOOL smb_register_vfs(const char *name, vfs_op_tuple *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *), int version)
158 struct vfs_init_function_entry *entry = backends;
160 if ((version < SMB_VFS_INTERFACE_CASCADED)) {
161 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
162 version, SMB_VFS_INTERFACE_VERSION ));
166 if ((version < SMB_VFS_INTERFACE_VERSION)) {
167 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
168 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
169 version, SMB_VFS_INTERFACE_VERSION, version ));
174 if (strequal(entry->name, name)) {
175 DEBUG(0,("VFS module %s already loaded!\n", name));
181 entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
182 entry->name = smb_xstrdup(name);
185 DLIST_ADD(backends, entry);
186 DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
190 /****************************************************************************
191 initialise default vfs hooks
192 ****************************************************************************/
194 static void vfs_init_default(connection_struct *conn)
196 DEBUG(3, ("Initialising default vfs hooks\n"));
198 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
199 conn->vfs_private = NULL;
202 /***************************************************************************
203 Function to load old VFS modules. Should go away after a while.
204 **************************************************************************/
206 static vfs_op_tuple *vfs_load_old_plugin(connection_struct *conn, const char *vfs_object)
208 int vfs_version = -1;
209 vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
210 /* Open object file */
212 if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
213 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
217 /* Get handle on vfs_init() symbol */
219 init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
221 if (init_fptr == NULL) {
222 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
223 sys_dlclose(conn->vfs_private->handle);
227 /* Initialise vfs_ops structure */
228 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
229 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
230 sys_dlclose(conn->vfs_private->handle);
234 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
235 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
236 vfs_version, SMB_VFS_INTERFACE_VERSION ));
237 sys_dlclose(conn->vfs_private->handle);
241 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
242 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
243 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
244 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
245 sys_dlclose(conn->vfs_private->handle);
254 /****************************************************************************
255 initialise custom vfs hooks
256 ****************************************************************************/
258 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
262 struct vfs_init_function_entry *entry;
264 DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
266 if(!backends) static_init_vfs;
268 /* First, try to load the module with the new module system */
269 if((entry = vfs_find_backend_entry(vfs_object)) ||
270 (smb_probe_module("vfs", vfs_object) &&
271 (entry = vfs_find_backend_entry(vfs_object)))) {
273 DEBUG(3,("Successfully loaded %s with the new modules system\n", vfs_object));
275 if ((ops = entry->init(&conn->vfs_ops, conn->vfs_private)) == NULL) {
276 DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
280 /* If that doesn't work, fall back to the old system
281 * (This part should go away after a while, it's only here
282 * for backwards compatibility) */
283 DEBUG(2, ("Can't load module %s with new modules system, falling back to compatibility\n",
285 if ((ops = vfs_load_old_plugin(conn, vfs_object)) == NULL) {
286 DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
291 for(i=0; ops[i].op != NULL; i++) {
292 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
293 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
294 /* Check whether this operation was already made opaque by different module */
295 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
296 /* No, it isn't overloaded yet. Overload. */
297 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
298 vfs_opaque_ops[ops[i].type] = ops[i];
301 /* Change current VFS disposition*/
302 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
303 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
309 /*****************************************************************
311 ******************************************************************/
313 BOOL smbd_vfs_init(connection_struct *conn)
315 const char **vfs_objects;
316 char *vfs_module, *vfs_path;
319 struct smb_vfs_handle_struct *handle;
321 /* Normal share - initialise with disk access functions */
322 vfs_init_default(conn);
323 vfs_objects = lp_vfsobj(SNUM(conn));
325 /* Override VFS functions if 'vfs object' was specified*/
329 for(i=0; i<SMB_VFS_OP_LAST; i++) {
330 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
331 vfs_opaque_ops[i].type = i;
332 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
335 vfs_path = lp_vfs_path(SNUM(conn));
337 for (j=0; vfs_objects[j]; j++) {
338 conn->vfs_private = NULL;
339 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
340 /* Loadable object file */
341 handle->handle = NULL;
342 DLIST_ADD(conn->vfs_private, handle);
344 if (vfs_path && *vfs_path) {
345 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[j]);
347 asprintf(&vfs_module, "%s", vfs_objects[j]);
349 if (!vfs_init_custom(conn, vfs_module)) {
350 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
351 SAFE_FREE(vfs_module);
352 DLIST_REMOVE(conn->vfs_private, handle);
356 SAFE_FREE(vfs_module);
361 /*******************************************************************
362 Create vfs_ops reflecting current vfs_opaque_ops
363 *******************************************************************/
365 struct vfs_ops *smb_vfs_get_opaque_ops(void)
370 ops = smb_xmalloc(sizeof(struct vfs_ops));
372 for(i=0; i<SMB_VFS_OP_LAST; i++) {
373 ((void**)ops)[i] = vfs_opaque_ops[i].op;
378 /*******************************************************************
379 Check if directory exists.
380 ********************************************************************/
382 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
390 if (vfs_stat(conn,dname,st) != 0)
393 ret = S_ISDIR(st->st_mode);
400 /*******************************************************************
402 ********************************************************************/
404 static char *vfs_getwd(connection_struct *conn, char *path)
406 return conn->vfs_ops.getwd(conn,path);
409 /*******************************************************************
411 ********************************************************************/
413 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
416 SMB_STRUCT_STAT sbuf;
418 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
420 inherit_access_acl(conn, name, mode);
423 * Check if high bits should have been set,
424 * then (if bits are missing): add them.
425 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
427 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
428 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
429 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
434 /*******************************************************************
435 Check if an object exists in the vfs.
436 ********************************************************************/
438 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
447 if (vfs_stat(conn,fname,sbuf) == -1)
452 /*******************************************************************
453 Check if a file exists in the vfs.
454 ********************************************************************/
456 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
465 if (vfs_stat(conn,fname,sbuf) == -1)
467 return(S_ISREG(sbuf->st_mode));
470 /****************************************************************************
471 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
472 ****************************************************************************/
474 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
478 while (total < byte_count)
480 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
483 if (ret == 0) return total;
492 return (ssize_t)total;
495 /****************************************************************************
496 Write data to a fd on the vfs.
497 ****************************************************************************/
499 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
505 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
514 return (ssize_t)total;
517 /****************************************************************************
518 An allocate file space call using the vfs interface.
519 Allocates space for a file from a filedescriptor.
520 Returns 0 on success, -1 on failure.
521 ****************************************************************************/
523 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
527 connection_struct *conn = fsp->conn;
528 struct vfs_ops *vfs_ops = &conn->vfs_ops;
529 SMB_BIG_UINT space_avail;
530 SMB_BIG_UINT bsize,dfree,dsize;
532 release_level_2_oplocks_on_change(fsp);
535 * Actually try and commit the space on disk....
538 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
540 if (((SMB_OFF_T)len) < 0) {
541 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
545 ret = vfs_fstat(fsp,fsp->fd,&st);
549 if (len == (SMB_BIG_UINT)st.st_size)
552 if (len < (SMB_BIG_UINT)st.st_size) {
553 /* Shrink - use ftruncate. */
555 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
556 fsp->fsp_name, (double)st.st_size ));
558 flush_write_cache(fsp, SIZECHANGE_FLUSH);
559 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
560 set_filelen_write_cache(fsp, len);
565 /* Grow - we need to test if we have enough space. */
567 if (!lp_strict_allocate(SNUM(fsp->conn)))
571 len /= 1024; /* Len is now number of 1k blocks needed. */
572 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
574 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
575 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
577 if (len > space_avail) {
585 /****************************************************************************
586 A vfs set_filelen call.
587 set the length of a file from a filedescriptor.
588 Returns 0 on success, -1 on failure.
589 ****************************************************************************/
591 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
595 release_level_2_oplocks_on_change(fsp);
596 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
597 flush_write_cache(fsp, SIZECHANGE_FLUSH);
598 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
599 set_filelen_write_cache(fsp, len);
604 /****************************************************************************
605 Transfer some data (n bytes) between two file_struct's.
606 ****************************************************************************/
608 static files_struct *in_fsp;
609 static files_struct *out_fsp;
611 static ssize_t read_fn(int fd, void *buf, size_t len)
613 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
616 static ssize_t write_fn(int fd, const void *buf, size_t len)
618 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
621 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
626 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
629 /*******************************************************************
630 A vfs_readdir wrapper which just returns the file name.
631 ********************************************************************/
633 char *vfs_readdirname(connection_struct *conn, void *p)
641 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
652 #ifdef HAVE_BROKEN_READDIR
653 /* using /usr/ucb/cc is BAD */
660 /* VFS options not quite working yet */
664 /***************************************************************************
665 handle the interpretation of the vfs option parameter
666 *************************************************************************/
667 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
669 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
672 /* Create new vfs option */
674 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
675 if (new_option == NULL) {
679 ZERO_STRUCTP(new_option);
681 /* Get name and value */
683 new_option->name = strtok(pszParmValue, "=");
685 if (new_option->name == NULL) {
689 while(isspace(*new_option->name)) {
693 for (i = strlen(new_option->name); i > 0; i--) {
694 if (!isspace(new_option->name[i - 1])) break;
697 new_option->name[i] = '\0';
698 new_option->name = strdup(new_option->name);
700 new_option->value = strtok(NULL, "=");
702 if (new_option->value != NULL) {
704 while(isspace(*new_option->value)) {
708 for (i = strlen(new_option->value); i > 0; i--) {
709 if (!isspace(new_option->value[i - 1])) break;
712 new_option->value[i] = '\0';
713 new_option->value = strdup(new_option->value);
718 DLIST_ADD(*options, new_option);
726 /*******************************************************************
727 A wrapper for vfs_chdir().
728 ********************************************************************/
730 int vfs_ChDir(connection_struct *conn, const char *path)
733 static pstring LastDir="";
735 if (strcsequal(path,"."))
738 if (*path == '/' && strcsequal(LastDir,path))
741 DEBUG(3,("vfs_ChDir to %s\n",path));
743 res = vfs_chdir(conn,path);
745 pstrcpy(LastDir,path);
749 /* number of list structures for a caching GetWd function. */
750 #define MAX_GETWDCACHE (50)
753 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
754 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
755 char *dos_path; /* The pathname in DOS format. */
757 } ino_list[MAX_GETWDCACHE];
759 extern BOOL use_getwd_cache;
761 /****************************************************************************
762 Prompte a ptr (to make it recently used)
763 ****************************************************************************/
765 static void array_promote(char *array,int elsize,int element)
771 p = (char *)malloc(elsize);
774 DEBUG(5,("array_promote: malloc fail\n"));
778 memcpy(p,array + element * elsize, elsize);
779 memmove(array + elsize,array,elsize*element);
780 memcpy(array,p,elsize);
784 /*******************************************************************
785 Return the absolute current directory path - given a UNIX pathname.
786 Note that this path is returned in DOS format, not UNIX
787 format. Note this can be called with conn == NULL.
788 ********************************************************************/
790 char *vfs_GetWd(connection_struct *conn, char *path)
793 static BOOL getwd_cache_init = False;
794 SMB_STRUCT_STAT st, st2;
799 if (!use_getwd_cache)
800 return(vfs_getwd(conn,path));
803 if (!getwd_cache_init) {
804 getwd_cache_init = True;
805 for (i=0;i<MAX_GETWDCACHE;i++) {
806 string_set(&ino_list[i].dos_path,"");
807 ino_list[i].valid = False;
811 /* Get the inode of the current directory, if this doesn't work we're
814 if (vfs_stat(conn, ".",&st) == -1) {
815 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
816 return(vfs_getwd(conn,path));
820 for (i=0; i<MAX_GETWDCACHE; i++) {
821 if (ino_list[i].valid) {
823 /* If we have found an entry with a matching inode and dev number
824 then find the inode number for the directory in the cached string.
825 If this agrees with that returned by the stat for the current
826 directory then all is o.k. (but make sure it is a directory all
829 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
830 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
831 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
832 (st2.st_mode & S_IFMT) == S_IFDIR) {
833 pstrcpy (path, ino_list[i].dos_path);
835 /* promote it for future use */
836 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
839 /* If the inode is different then something's changed,
840 scrub the entry and start from scratch. */
841 ino_list[i].valid = False;
848 /* We don't have the information to hand so rely on traditional methods.
849 The very slow getcwd, which spawns a process on some systems, or the
850 not quite so bad getwd. */
852 if (!vfs_getwd(conn,s)) {
853 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
859 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
861 /* add it to the cache */
862 i = MAX_GETWDCACHE - 1;
863 string_set(&ino_list[i].dos_path,s);
864 ino_list[i].dev = st.st_dev;
865 ino_list[i].inode = st.st_ino;
866 ino_list[i].valid = True;
868 /* put it at the top of the list */
869 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
875 /* check if the file 'nmae' is a symlink, in that case check that it point to
876 a file that reside under the 'dir' tree */
878 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
887 if (!vfs_GetWd(conn, savedir)) {
888 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
892 if (vfs_ChDir(conn, dir) != 0) {
893 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
897 if (!vfs_GetWd(conn, realdir)) {
898 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
899 vfs_ChDir(conn, savedir);
903 reallen = strlen(realdir);
904 if (realdir[reallen -1] == '/') {
906 realdir[reallen] = 0;
909 if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
910 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
912 pstrcpy(cleanlink, flink);
914 pstrcpy(cleanlink, realdir);
915 pstrcat(cleanlink, "/");
916 pstrcat(cleanlink, flink);
918 unix_clean_name(cleanlink);
920 if (strncmp(cleanlink, realdir, reallen) != 0) {
921 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
926 vfs_ChDir(conn, savedir);
931 /*******************************************************************
932 Reduce a file name, removing .. elements and checking that
933 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
934 on the system that has the referenced file system.
935 Widelinks are allowed if widelinks is true.
936 ********************************************************************/
938 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
948 BOOL relative = (*s != '/');
950 *dir2 = *wd = *base_name = *newname = 0;
954 /* can't have a leading .. */
955 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
956 DEBUG(3,("Illegal file name? (%s)\n",s));
966 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
968 /* remove any double slashes */
969 all_string_sub(s,"//","/",0);
971 pstrcpy(base_name,s);
972 p = strrchr_m(base_name,'/');
975 return readlink_check(conn, dir, s);
977 if (!vfs_GetWd(conn,wd)) {
978 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
982 if (vfs_ChDir(conn,dir) != 0) {
983 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
987 if (!vfs_GetWd(conn,dir2)) {
988 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
993 if (p && (p != base_name)) {
995 if (strcmp(p+1,".")==0)
997 if (strcmp(p+1,"..")==0)
1001 if (vfs_ChDir(conn,base_name) != 0) {
1003 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
1007 if (!vfs_GetWd(conn,newname)) {
1009 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
1013 if (p && (p != base_name)) {
1014 pstrcat(newname,"/");
1015 pstrcat(newname,p+1);
1019 size_t l = strlen(dir2);
1020 if (dir2[l-1] == '/')
1023 if (strncmp(newname,dir2,l) != 0) {
1025 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
1029 if (!readlink_check(conn, dir, newname)) {
1030 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
1035 if (newname[l] == '/')
1036 pstrcpy(s,newname + l + 1);
1038 pstrcpy(s,newname+l);
1048 DEBUG(3,("reduced to %s\n",s));