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 (i=0; vfs_objects[i]; i++); /* count passed modules */
339 for (j=i-1; j >= 0; j--) {
340 conn->vfs_private = NULL;
341 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
342 /* Loadable object file */
343 handle->handle = NULL;
344 DLIST_ADD(conn->vfs_private, handle);
346 if (vfs_path && *vfs_path) {
347 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[j]);
349 asprintf(&vfs_module, "%s", vfs_objects[j]);
351 if (!vfs_init_custom(conn, vfs_module)) {
352 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
353 SAFE_FREE(vfs_module);
354 DLIST_REMOVE(conn->vfs_private, handle);
358 SAFE_FREE(vfs_module);
363 /*******************************************************************
364 Create vfs_ops reflecting current vfs_opaque_ops
365 *******************************************************************/
367 struct vfs_ops *smb_vfs_get_opaque_ops(void)
372 ops = smb_xmalloc(sizeof(struct vfs_ops));
374 for(i=0; i<SMB_VFS_OP_LAST; i++) {
375 ((void**)ops)[i] = vfs_opaque_ops[i].op;
380 /*******************************************************************
381 Check if directory exists.
382 ********************************************************************/
384 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
392 if (vfs_stat(conn,dname,st) != 0)
395 ret = S_ISDIR(st->st_mode);
402 /*******************************************************************
404 ********************************************************************/
406 static char *vfs_getwd(connection_struct *conn, char *path)
408 return conn->vfs_ops.getwd(conn,path);
411 /*******************************************************************
413 ********************************************************************/
415 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
418 SMB_STRUCT_STAT sbuf;
420 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
422 inherit_access_acl(conn, name, mode);
425 * Check if high bits should have been set,
426 * then (if bits are missing): add them.
427 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
429 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
430 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
431 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
436 /*******************************************************************
437 Check if an object exists in the vfs.
438 ********************************************************************/
440 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
449 if (vfs_stat(conn,fname,sbuf) == -1)
454 /*******************************************************************
455 Check if a file exists in the vfs.
456 ********************************************************************/
458 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
467 if (vfs_stat(conn,fname,sbuf) == -1)
469 return(S_ISREG(sbuf->st_mode));
472 /****************************************************************************
473 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
474 ****************************************************************************/
476 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
480 while (total < byte_count)
482 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
485 if (ret == 0) return total;
494 return (ssize_t)total;
497 /****************************************************************************
498 Write data to a fd on the vfs.
499 ****************************************************************************/
501 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
507 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
516 return (ssize_t)total;
519 /****************************************************************************
520 An allocate file space call using the vfs interface.
521 Allocates space for a file from a filedescriptor.
522 Returns 0 on success, -1 on failure.
523 ****************************************************************************/
525 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
529 connection_struct *conn = fsp->conn;
530 struct vfs_ops *vfs_ops = &conn->vfs_ops;
531 SMB_BIG_UINT space_avail;
532 SMB_BIG_UINT bsize,dfree,dsize;
534 release_level_2_oplocks_on_change(fsp);
537 * Actually try and commit the space on disk....
540 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
542 if (((SMB_OFF_T)len) < 0) {
543 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
547 ret = vfs_fstat(fsp,fsp->fd,&st);
551 if (len == (SMB_BIG_UINT)st.st_size)
554 if (len < (SMB_BIG_UINT)st.st_size) {
555 /* Shrink - use ftruncate. */
557 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
558 fsp->fsp_name, (double)st.st_size ));
560 flush_write_cache(fsp, SIZECHANGE_FLUSH);
561 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
562 set_filelen_write_cache(fsp, len);
567 /* Grow - we need to test if we have enough space. */
569 if (!lp_strict_allocate(SNUM(fsp->conn)))
573 len /= 1024; /* Len is now number of 1k blocks needed. */
574 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
576 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
577 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
579 if (len > space_avail) {
587 /****************************************************************************
588 A vfs set_filelen call.
589 set the length of a file from a filedescriptor.
590 Returns 0 on success, -1 on failure.
591 ****************************************************************************/
593 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
597 release_level_2_oplocks_on_change(fsp);
598 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
599 flush_write_cache(fsp, SIZECHANGE_FLUSH);
600 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
601 set_filelen_write_cache(fsp, len);
606 /****************************************************************************
607 Transfer some data (n bytes) between two file_struct's.
608 ****************************************************************************/
610 static files_struct *in_fsp;
611 static files_struct *out_fsp;
613 static ssize_t read_fn(int fd, void *buf, size_t len)
615 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
618 static ssize_t write_fn(int fd, const void *buf, size_t len)
620 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
623 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
628 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
631 /*******************************************************************
632 A vfs_readdir wrapper which just returns the file name.
633 ********************************************************************/
635 char *vfs_readdirname(connection_struct *conn, void *p)
643 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
654 #ifdef HAVE_BROKEN_READDIR
655 /* using /usr/ucb/cc is BAD */
662 /* VFS options not quite working yet */
666 /***************************************************************************
667 handle the interpretation of the vfs option parameter
668 *************************************************************************/
669 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
671 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
674 /* Create new vfs option */
676 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
677 if (new_option == NULL) {
681 ZERO_STRUCTP(new_option);
683 /* Get name and value */
685 new_option->name = strtok(pszParmValue, "=");
687 if (new_option->name == NULL) {
691 while(isspace(*new_option->name)) {
695 for (i = strlen(new_option->name); i > 0; i--) {
696 if (!isspace(new_option->name[i - 1])) break;
699 new_option->name[i] = '\0';
700 new_option->name = strdup(new_option->name);
702 new_option->value = strtok(NULL, "=");
704 if (new_option->value != NULL) {
706 while(isspace(*new_option->value)) {
710 for (i = strlen(new_option->value); i > 0; i--) {
711 if (!isspace(new_option->value[i - 1])) break;
714 new_option->value[i] = '\0';
715 new_option->value = strdup(new_option->value);
720 DLIST_ADD(*options, new_option);
728 /*******************************************************************
729 A wrapper for vfs_chdir().
730 ********************************************************************/
732 int vfs_ChDir(connection_struct *conn, const char *path)
735 static pstring LastDir="";
737 if (strcsequal(path,"."))
740 if (*path == '/' && strcsequal(LastDir,path))
743 DEBUG(3,("vfs_ChDir to %s\n",path));
745 res = vfs_chdir(conn,path);
747 pstrcpy(LastDir,path);
751 /* number of list structures for a caching GetWd function. */
752 #define MAX_GETWDCACHE (50)
755 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
756 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
757 char *dos_path; /* The pathname in DOS format. */
759 } ino_list[MAX_GETWDCACHE];
761 extern BOOL use_getwd_cache;
763 /****************************************************************************
764 Prompte a ptr (to make it recently used)
765 ****************************************************************************/
767 static void array_promote(char *array,int elsize,int element)
773 p = (char *)malloc(elsize);
776 DEBUG(5,("array_promote: malloc fail\n"));
780 memcpy(p,array + element * elsize, elsize);
781 memmove(array + elsize,array,elsize*element);
782 memcpy(array,p,elsize);
786 /*******************************************************************
787 Return the absolute current directory path - given a UNIX pathname.
788 Note that this path is returned in DOS format, not UNIX
789 format. Note this can be called with conn == NULL.
790 ********************************************************************/
792 char *vfs_GetWd(connection_struct *conn, char *path)
795 static BOOL getwd_cache_init = False;
796 SMB_STRUCT_STAT st, st2;
801 if (!use_getwd_cache)
802 return(vfs_getwd(conn,path));
805 if (!getwd_cache_init) {
806 getwd_cache_init = True;
807 for (i=0;i<MAX_GETWDCACHE;i++) {
808 string_set(&ino_list[i].dos_path,"");
809 ino_list[i].valid = False;
813 /* Get the inode of the current directory, if this doesn't work we're
816 if (vfs_stat(conn, ".",&st) == -1) {
817 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
818 return(vfs_getwd(conn,path));
822 for (i=0; i<MAX_GETWDCACHE; i++) {
823 if (ino_list[i].valid) {
825 /* If we have found an entry with a matching inode and dev number
826 then find the inode number for the directory in the cached string.
827 If this agrees with that returned by the stat for the current
828 directory then all is o.k. (but make sure it is a directory all
831 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
832 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
833 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
834 (st2.st_mode & S_IFMT) == S_IFDIR) {
835 pstrcpy (path, ino_list[i].dos_path);
837 /* promote it for future use */
838 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
841 /* If the inode is different then something's changed,
842 scrub the entry and start from scratch. */
843 ino_list[i].valid = False;
850 /* We don't have the information to hand so rely on traditional methods.
851 The very slow getcwd, which spawns a process on some systems, or the
852 not quite so bad getwd. */
854 if (!vfs_getwd(conn,s)) {
855 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
861 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
863 /* add it to the cache */
864 i = MAX_GETWDCACHE - 1;
865 string_set(&ino_list[i].dos_path,s);
866 ino_list[i].dev = st.st_dev;
867 ino_list[i].inode = st.st_ino;
868 ino_list[i].valid = True;
870 /* put it at the top of the list */
871 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
877 /* check if the file 'nmae' is a symlink, in that case check that it point to
878 a file that reside under the 'dir' tree */
880 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
889 if (!vfs_GetWd(conn, savedir)) {
890 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
894 if (vfs_ChDir(conn, dir) != 0) {
895 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
899 if (!vfs_GetWd(conn, realdir)) {
900 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
901 vfs_ChDir(conn, savedir);
905 reallen = strlen(realdir);
906 if (realdir[reallen -1] == '/') {
908 realdir[reallen] = 0;
911 if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
912 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
914 pstrcpy(cleanlink, flink);
916 pstrcpy(cleanlink, realdir);
917 pstrcat(cleanlink, "/");
918 pstrcat(cleanlink, flink);
920 unix_clean_name(cleanlink);
922 if (strncmp(cleanlink, realdir, reallen) != 0) {
923 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
928 vfs_ChDir(conn, savedir);
933 /*******************************************************************
934 Reduce a file name, removing .. elements and checking that
935 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
936 on the system that has the referenced file system.
937 Widelinks are allowed if widelinks is true.
938 ********************************************************************/
940 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
950 BOOL relative = (*s != '/');
952 *dir2 = *wd = *base_name = *newname = 0;
956 /* can't have a leading .. */
957 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
958 DEBUG(3,("Illegal file name? (%s)\n",s));
968 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
970 /* remove any double slashes */
971 all_string_sub(s,"//","/",0);
973 pstrcpy(base_name,s);
974 p = strrchr_m(base_name,'/');
977 return readlink_check(conn, dir, s);
979 if (!vfs_GetWd(conn,wd)) {
980 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
984 if (vfs_ChDir(conn,dir) != 0) {
985 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
989 if (!vfs_GetWd(conn,dir2)) {
990 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
995 if (p && (p != base_name)) {
997 if (strcmp(p+1,".")==0)
999 if (strcmp(p+1,"..")==0)
1003 if (vfs_ChDir(conn,base_name) != 0) {
1005 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
1009 if (!vfs_GetWd(conn,newname)) {
1011 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
1015 if (p && (p != base_name)) {
1016 pstrcat(newname,"/");
1017 pstrcat(newname,p+1);
1021 size_t l = strlen(dir2);
1022 if (dir2[l-1] == '/')
1025 if (strncmp(newname,dir2,l) != 0) {
1027 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
1031 if (!readlink_check(conn, dir, newname)) {
1032 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
1037 if (newname[l] == '/')
1038 pstrcpy(s,newname + l + 1);
1040 pstrcpy(s,newname+l);
1050 DEBUG(3,("reduced to %s\n",s));