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 static 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 NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *))
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 ));
163 return NT_STATUS_OBJECT_TYPE_MISMATCH;
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 ));
170 return NT_STATUS_OBJECT_TYPE_MISMATCH;
173 if (!name || !init) {
174 return NT_STATUS_INVALID_PARAMETER;
177 if (vfs_find_backend_entry(name)) {
178 DEBUG(0,("VFS module %s already loaded!\n", name));
179 return NT_STATUS_OBJECT_NAME_COLLISION;
182 entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
183 entry->name = smb_xstrdup(name);
186 DLIST_ADD(backends, entry);
187 DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
191 /****************************************************************************
192 initialise default vfs hooks
193 ****************************************************************************/
195 static void vfs_init_default(connection_struct *conn)
197 DEBUG(3, ("Initialising default vfs hooks\n"));
199 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
200 conn->vfs_private = NULL;
203 /***************************************************************************
204 Function to load old VFS modules. Should go away after a while.
205 **************************************************************************/
207 static vfs_op_tuple *vfs_load_old_plugin(connection_struct *conn, const char *vfs_object)
209 int vfs_version = -1;
210 vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
211 /* Open object file */
213 if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
214 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
218 /* Get handle on vfs_init() symbol */
220 init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
222 if (init_fptr == NULL) {
223 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
224 sys_dlclose(conn->vfs_private->handle);
228 /* Initialise vfs_ops structure */
229 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
230 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
231 sys_dlclose(conn->vfs_private->handle);
235 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
236 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
237 vfs_version, SMB_VFS_INTERFACE_VERSION ));
238 sys_dlclose(conn->vfs_private->handle);
242 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
243 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
244 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
245 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
246 sys_dlclose(conn->vfs_private->handle);
255 /****************************************************************************
256 initialise custom vfs hooks
257 ****************************************************************************/
259 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
263 struct vfs_init_function_entry *entry;
265 DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
267 if(!backends) static_init_vfs;
269 /* First, try to load the module with the new module system */
270 if((entry = vfs_find_backend_entry(vfs_object)) ||
271 (NT_STATUS_IS_OK(smb_probe_module("vfs", vfs_object)) &&
272 (entry = vfs_find_backend_entry(vfs_object)))) {
274 DEBUG(3,("Successfully loaded %s with the new modules system\n", vfs_object));
276 if ((ops = entry->init(&conn->vfs_ops, conn->vfs_private)) == NULL) {
277 DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
281 /* If that doesn't work, fall back to the old system
282 * (This part should go away after a while, it's only here
283 * for backwards compatibility) */
284 DEBUG(2, ("Can't load module %s with new modules system, falling back to compatibility\n",
286 if ((ops = vfs_load_old_plugin(conn, vfs_object)) == NULL) {
287 DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
292 for(i=0; ops[i].op != NULL; i++) {
293 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
294 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
295 /* Check whether this operation was already made opaque by different module */
296 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
297 /* No, it isn't overloaded yet. Overload. */
298 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
299 vfs_opaque_ops[ops[i].type] = ops[i];
302 /* Change current VFS disposition*/
303 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
304 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
310 /*****************************************************************
312 ******************************************************************/
314 BOOL smbd_vfs_init(connection_struct *conn)
316 const char **vfs_objects;
317 char *vfs_module, *vfs_path;
320 struct smb_vfs_handle_struct *handle;
322 /* Normal share - initialise with disk access functions */
323 vfs_init_default(conn);
324 vfs_objects = lp_vfsobj(SNUM(conn));
326 /* Override VFS functions if 'vfs object' was not specified*/
327 if (!vfs_objects || !vfs_objects[0])
330 for(i=0; i<SMB_VFS_OP_LAST; i++) {
331 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
332 vfs_opaque_ops[i].type = i;
333 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
336 vfs_path = lp_vfs_path(SNUM(conn));
338 for (i=0; vfs_objects[i]; i++); /* count passed modules */
340 for (j=i-1; j >= 0; j--) {
341 conn->vfs_private = NULL;
342 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
343 /* Loadable object file */
344 handle->handle = NULL;
345 DLIST_ADD(conn->vfs_private, handle);
347 if (vfs_path && *vfs_path) {
348 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[j]);
350 asprintf(&vfs_module, "%s", vfs_objects[j]);
352 if (!vfs_init_custom(conn, vfs_module)) {
353 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
354 SAFE_FREE(vfs_module);
355 DLIST_REMOVE(conn->vfs_private, handle);
359 SAFE_FREE(vfs_module);
364 /*******************************************************************
365 Create vfs_ops reflecting current vfs_opaque_ops
366 *******************************************************************/
368 struct vfs_ops *smb_vfs_get_opaque_ops(void)
373 ops = smb_xmalloc(sizeof(struct vfs_ops));
375 for(i=0; i<SMB_VFS_OP_LAST; i++) {
376 ((void**)ops)[i] = vfs_opaque_ops[i].op;
381 /*******************************************************************
382 Check if directory exists.
383 ********************************************************************/
385 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
393 if (vfs_stat(conn,dname,st) != 0)
396 ret = S_ISDIR(st->st_mode);
403 /*******************************************************************
405 ********************************************************************/
407 static char *vfs_getwd(connection_struct *conn, char *path)
409 return conn->vfs_ops.getwd(conn,path);
412 /*******************************************************************
414 ********************************************************************/
416 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
419 SMB_STRUCT_STAT sbuf;
421 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
423 inherit_access_acl(conn, name, mode);
426 * Check if high bits should have been set,
427 * then (if bits are missing): add them.
428 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
430 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
431 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
432 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
437 /*******************************************************************
438 Check if an object exists in the vfs.
439 ********************************************************************/
441 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
450 if (vfs_stat(conn,fname,sbuf) == -1)
455 /*******************************************************************
456 Check if a file exists in the vfs.
457 ********************************************************************/
459 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
468 if (vfs_stat(conn,fname,sbuf) == -1)
470 return(S_ISREG(sbuf->st_mode));
473 /****************************************************************************
474 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
475 ****************************************************************************/
477 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
481 while (total < byte_count)
483 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
486 if (ret == 0) return total;
495 return (ssize_t)total;
498 /****************************************************************************
499 Write data to a fd on the vfs.
500 ****************************************************************************/
502 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
508 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
517 return (ssize_t)total;
520 /****************************************************************************
521 An allocate file space call using the vfs interface.
522 Allocates space for a file from a filedescriptor.
523 Returns 0 on success, -1 on failure.
524 ****************************************************************************/
526 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
530 connection_struct *conn = fsp->conn;
531 struct vfs_ops *vfs_ops = &conn->vfs_ops;
532 SMB_BIG_UINT space_avail;
533 SMB_BIG_UINT bsize,dfree,dsize;
535 release_level_2_oplocks_on_change(fsp);
538 * Actually try and commit the space on disk....
541 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
543 if (((SMB_OFF_T)len) < 0) {
544 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
548 ret = vfs_fstat(fsp,fsp->fd,&st);
552 if (len == (SMB_BIG_UINT)st.st_size)
555 if (len < (SMB_BIG_UINT)st.st_size) {
556 /* Shrink - use ftruncate. */
558 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
559 fsp->fsp_name, (double)st.st_size ));
561 flush_write_cache(fsp, SIZECHANGE_FLUSH);
562 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
563 set_filelen_write_cache(fsp, len);
568 /* Grow - we need to test if we have enough space. */
570 if (!lp_strict_allocate(SNUM(fsp->conn)))
574 len /= 1024; /* Len is now number of 1k blocks needed. */
575 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
577 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
578 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
580 if (len > space_avail) {
588 /****************************************************************************
589 A vfs set_filelen call.
590 set the length of a file from a filedescriptor.
591 Returns 0 on success, -1 on failure.
592 ****************************************************************************/
594 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
598 release_level_2_oplocks_on_change(fsp);
599 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
600 flush_write_cache(fsp, SIZECHANGE_FLUSH);
601 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
602 set_filelen_write_cache(fsp, len);
607 /****************************************************************************
608 Transfer some data (n bytes) between two file_struct's.
609 ****************************************************************************/
611 static files_struct *in_fsp;
612 static files_struct *out_fsp;
614 static ssize_t read_fn(int fd, void *buf, size_t len)
616 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
619 static ssize_t write_fn(int fd, const void *buf, size_t len)
621 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
624 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
629 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
632 /*******************************************************************
633 A vfs_readdir wrapper which just returns the file name.
634 ********************************************************************/
636 char *vfs_readdirname(connection_struct *conn, void *p)
644 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
655 #ifdef HAVE_BROKEN_READDIR
656 /* using /usr/ucb/cc is BAD */
663 /* VFS options not quite working yet */
667 /***************************************************************************
668 handle the interpretation of the vfs option parameter
669 *************************************************************************/
670 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
672 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
675 /* Create new vfs option */
677 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
678 if (new_option == NULL) {
682 ZERO_STRUCTP(new_option);
684 /* Get name and value */
686 new_option->name = strtok(pszParmValue, "=");
688 if (new_option->name == NULL) {
692 while(isspace(*new_option->name)) {
696 for (i = strlen(new_option->name); i > 0; i--) {
697 if (!isspace(new_option->name[i - 1])) break;
700 new_option->name[i] = '\0';
701 new_option->name = strdup(new_option->name);
703 new_option->value = strtok(NULL, "=");
705 if (new_option->value != NULL) {
707 while(isspace(*new_option->value)) {
711 for (i = strlen(new_option->value); i > 0; i--) {
712 if (!isspace(new_option->value[i - 1])) break;
715 new_option->value[i] = '\0';
716 new_option->value = strdup(new_option->value);
721 DLIST_ADD(*options, new_option);
729 /*******************************************************************
730 A wrapper for vfs_chdir().
731 ********************************************************************/
733 int vfs_ChDir(connection_struct *conn, const char *path)
736 static pstring LastDir="";
738 if (strcsequal(path,"."))
741 if (*path == '/' && strcsequal(LastDir,path))
744 DEBUG(3,("vfs_ChDir to %s\n",path));
746 res = vfs_chdir(conn,path);
748 pstrcpy(LastDir,path);
752 /* number of list structures for a caching GetWd function. */
753 #define MAX_GETWDCACHE (50)
756 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
757 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
758 char *dos_path; /* The pathname in DOS format. */
760 } ino_list[MAX_GETWDCACHE];
762 extern BOOL use_getwd_cache;
764 /****************************************************************************
765 Prompte a ptr (to make it recently used)
766 ****************************************************************************/
768 static void array_promote(char *array,int elsize,int element)
774 p = (char *)malloc(elsize);
777 DEBUG(5,("array_promote: malloc fail\n"));
781 memcpy(p,array + element * elsize, elsize);
782 memmove(array + elsize,array,elsize*element);
783 memcpy(array,p,elsize);
787 /*******************************************************************
788 Return the absolute current directory path - given a UNIX pathname.
789 Note that this path is returned in DOS format, not UNIX
790 format. Note this can be called with conn == NULL.
791 ********************************************************************/
793 char *vfs_GetWd(connection_struct *conn, char *path)
796 static BOOL getwd_cache_init = False;
797 SMB_STRUCT_STAT st, st2;
802 if (!use_getwd_cache)
803 return(vfs_getwd(conn,path));
806 if (!getwd_cache_init) {
807 getwd_cache_init = True;
808 for (i=0;i<MAX_GETWDCACHE;i++) {
809 string_set(&ino_list[i].dos_path,"");
810 ino_list[i].valid = False;
814 /* Get the inode of the current directory, if this doesn't work we're
817 if (vfs_stat(conn, ".",&st) == -1) {
818 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
819 return(vfs_getwd(conn,path));
823 for (i=0; i<MAX_GETWDCACHE; i++) {
824 if (ino_list[i].valid) {
826 /* If we have found an entry with a matching inode and dev number
827 then find the inode number for the directory in the cached string.
828 If this agrees with that returned by the stat for the current
829 directory then all is o.k. (but make sure it is a directory all
832 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
833 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
834 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
835 (st2.st_mode & S_IFMT) == S_IFDIR) {
836 pstrcpy (path, ino_list[i].dos_path);
838 /* promote it for future use */
839 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
842 /* If the inode is different then something's changed,
843 scrub the entry and start from scratch. */
844 ino_list[i].valid = False;
851 /* We don't have the information to hand so rely on traditional methods.
852 The very slow getcwd, which spawns a process on some systems, or the
853 not quite so bad getwd. */
855 if (!vfs_getwd(conn,s)) {
856 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
862 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
864 /* add it to the cache */
865 i = MAX_GETWDCACHE - 1;
866 string_set(&ino_list[i].dos_path,s);
867 ino_list[i].dev = st.st_dev;
868 ino_list[i].inode = st.st_ino;
869 ino_list[i].valid = True;
871 /* put it at the top of the list */
872 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
878 /* check if the file 'nmae' is a symlink, in that case check that it point to
879 a file that reside under the 'dir' tree */
881 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
890 if (!vfs_GetWd(conn, savedir)) {
891 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
895 if (vfs_ChDir(conn, dir) != 0) {
896 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
900 if (!vfs_GetWd(conn, realdir)) {
901 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
902 vfs_ChDir(conn, savedir);
906 reallen = strlen(realdir);
907 if (realdir[reallen -1] == '/') {
909 realdir[reallen] = 0;
912 if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
913 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
915 pstrcpy(cleanlink, flink);
917 pstrcpy(cleanlink, realdir);
918 pstrcat(cleanlink, "/");
919 pstrcat(cleanlink, flink);
921 unix_clean_name(cleanlink);
923 if (strncmp(cleanlink, realdir, reallen) != 0) {
924 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
929 vfs_ChDir(conn, savedir);
934 /*******************************************************************
935 Reduce a file name, removing .. elements and checking that
936 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
937 on the system that has the referenced file system.
938 Widelinks are allowed if widelinks is true.
939 ********************************************************************/
941 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
951 BOOL relative = (*s != '/');
953 *dir2 = *wd = *base_name = *newname = 0;
957 /* can't have a leading .. */
958 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
959 DEBUG(3,("Illegal file name? (%s)\n",s));
969 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
971 /* remove any double slashes */
972 all_string_sub(s,"//","/",0);
974 pstrcpy(base_name,s);
975 p = strrchr_m(base_name,'/');
978 return readlink_check(conn, dir, s);
980 if (!vfs_GetWd(conn,wd)) {
981 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
985 if (vfs_ChDir(conn,dir) != 0) {
986 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
990 if (!vfs_GetWd(conn,dir2)) {
991 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
996 if (p && (p != base_name)) {
998 if (strcmp(p+1,".")==0)
1000 if (strcmp(p+1,"..")==0)
1004 if (vfs_ChDir(conn,base_name) != 0) {
1006 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
1010 if (!vfs_GetWd(conn,newname)) {
1012 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
1016 if (p && (p != base_name)) {
1017 pstrcat(newname,"/");
1018 pstrcat(newname,p+1);
1022 size_t l = strlen(dir2);
1023 if (dir2[l-1] == '/')
1026 if (strncmp(newname,dir2,l) != 0) {
1028 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
1032 if (!readlink_check(conn, dir, newname)) {
1033 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
1038 if (newname[l] == '/')
1039 pstrcpy(s,newname + l + 1);
1041 pstrcpy(s,newname+l);
1051 DEBUG(3,("reduced to %s\n",s));