adding some initiaial code to sert %a to Win2K3 (using Native LanMan string from...
[Samba.git] / source / smbd / vfs.c
blob533c64b229fc8b0851ac96c8bf7fbec43f411fb9
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 VFS initialisation and support functions
5 Copyright (C) Tim Potter 1999
6 Copyright (C) Alexander Bokovoy 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 This work was sponsored by Optifacio Software Services, Inc.
25 #include "includes.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_VFS
31 /* Some structures to help us initialise the vfs operations table */
33 struct vfs_syminfo {
34 char *name;
35 void *fptr;
39 Opaque (final) vfs operations. This is a combination of first-met opaque vfs operations
40 across all currently processed modules. */
42 static vfs_op_tuple vfs_opaque_ops[SMB_VFS_OP_LAST];
44 /* Default vfs hooks. WARNING: The order of these initialisers is
45 very important. They must be in the same order as defined in
46 vfs.h. Change at your own peril. */
48 static struct vfs_ops default_vfs_ops = {
50 /* Disk operations */
52 vfswrap_dummy_connect,
53 vfswrap_dummy_disconnect,
54 vfswrap_disk_free,
56 /* Directory operations */
58 vfswrap_opendir,
59 vfswrap_readdir,
60 vfswrap_mkdir,
61 vfswrap_rmdir,
62 vfswrap_closedir,
64 /* File operations */
66 vfswrap_open,
67 vfswrap_close,
68 vfswrap_read,
69 vfswrap_write,
70 vfswrap_lseek,
71 vfswrap_sendfile,
72 vfswrap_rename,
73 vfswrap_fsync,
74 vfswrap_stat,
75 vfswrap_fstat,
76 vfswrap_lstat,
77 vfswrap_unlink,
78 vfswrap_chmod,
79 vfswrap_fchmod,
80 vfswrap_chown,
81 vfswrap_fchown,
82 vfswrap_chdir,
83 vfswrap_getwd,
84 vfswrap_utime,
85 vfswrap_ftruncate,
86 vfswrap_lock,
87 vfswrap_symlink,
88 vfswrap_readlink,
89 vfswrap_link,
90 vfswrap_mknod,
91 vfswrap_realpath,
93 vfswrap_fget_nt_acl,
94 vfswrap_get_nt_acl,
95 vfswrap_fset_nt_acl,
96 vfswrap_set_nt_acl,
98 /* POSIX ACL operations. */
99 #if defined(HAVE_NO_ACLS)
100 NULL,
101 NULL,
102 #else
103 vfswrap_chmod_acl,
104 vfswrap_fchmod_acl,
105 #endif
106 vfswrap_sys_acl_get_entry,
107 vfswrap_sys_acl_get_tag_type,
108 vfswrap_sys_acl_get_permset,
109 vfswrap_sys_acl_get_qualifier,
110 vfswrap_sys_acl_get_file,
111 vfswrap_sys_acl_get_fd,
112 vfswrap_sys_acl_clear_perms,
113 vfswrap_sys_acl_add_perm,
114 vfswrap_sys_acl_to_text,
115 vfswrap_sys_acl_init,
116 vfswrap_sys_acl_create_entry,
117 vfswrap_sys_acl_set_tag_type,
118 vfswrap_sys_acl_set_qualifier,
119 vfswrap_sys_acl_set_permset,
120 vfswrap_sys_acl_valid,
121 vfswrap_sys_acl_set_file,
122 vfswrap_sys_acl_set_fd,
123 vfswrap_sys_acl_delete_def_file,
124 vfswrap_sys_acl_get_perm,
125 vfswrap_sys_acl_free_text,
126 vfswrap_sys_acl_free_acl,
127 vfswrap_sys_acl_free_qualifier
130 /****************************************************************************
131 initialise default vfs hooks
132 ****************************************************************************/
134 static void vfs_init_default(connection_struct *conn)
136 DEBUG(3, ("Initialising default vfs hooks\n"));
138 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
139 conn->vfs_private = NULL;
142 /****************************************************************************
143 initialise custom vfs hooks
144 ****************************************************************************/
146 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
148 int vfs_version = -1;
149 vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
150 int i;
152 DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
154 /* Open object file */
156 if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
157 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
158 return False;
161 /* Get handle on vfs_init() symbol */
163 init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
165 if (init_fptr == NULL) {
166 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
167 sys_dlclose(conn->vfs_private->handle);
168 return False;
171 /* Initialise vfs_ops structure */
173 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
174 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
175 sys_dlclose(conn->vfs_private->handle);
176 return False;
179 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
180 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
181 vfs_version, SMB_VFS_INTERFACE_VERSION ));
182 sys_dlclose(conn->vfs_private->handle);
183 return False;
186 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
187 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
188 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
189 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
190 sys_dlclose(conn->vfs_private->handle);
191 return False;
194 for(i=0; ops[i].op != NULL; i++) {
195 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
196 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
197 /* Check whether this operation was already made opaque by different module */
198 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
199 /* No, it isn't overloaded yet. Overload. */
200 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
201 vfs_opaque_ops[ops[i].type] = ops[i];
204 /* Change current VFS disposition*/
205 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
206 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
209 return True;
212 /*****************************************************************
213 Generic VFS init.
214 ******************************************************************/
216 BOOL smbd_vfs_init(connection_struct *conn)
218 char **vfs_objects, *vfsobj, *vfs_module, *vfs_path;
219 int nobj, i;
220 struct smb_vfs_handle_struct *handle;
222 /* Normal share - initialise with disk access functions */
223 vfs_init_default(conn);
225 /* Override VFS functions if 'vfs object' was specified*/
226 if (*lp_vfsobj(SNUM(conn))) {
227 vfsobj = NULL;
228 for(i=0; i<SMB_VFS_OP_LAST; i++) {
229 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
230 vfs_opaque_ops[i].type = i;
231 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
233 if (string_set(&vfsobj, lp_vfsobj(SNUM(conn)))) {
234 /* Parse passed modules specification to array of modules */
235 set_first_token(vfsobj);
236 /* We are using default separators: ' \t\r\n' */
237 vfs_objects = toktocliplist(&nobj, NULL);
238 if (vfs_objects) {
239 vfs_path = lp_vfs_path(SNUM(conn));
240 conn->vfs_private = NULL;
241 for(i=nobj-1; i>=0; i--) {
242 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
243 /* Loadable object file */
244 handle->handle = NULL;
245 DLIST_ADD(conn->vfs_private, handle)
246 vfs_module = NULL;
247 if (vfs_path) {
248 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[i]);
249 } else {
250 asprintf(&vfs_module, "%s", vfs_objects[i]);
252 if (!vfs_init_custom(conn, vfs_module)) {
253 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
254 string_free(&vfsobj);
255 SAFE_FREE(vfs_module);
256 DLIST_REMOVE(conn->vfs_private, handle);
257 SAFE_FREE(handle);
258 return False;
260 SAFE_FREE(vfs_module);
263 string_free(&vfsobj);
264 return True;
267 return True;
270 /*******************************************************************
271 Create vfs_ops reflecting current vfs_opaque_ops
272 *******************************************************************/
274 struct vfs_ops *smb_vfs_get_opaque_ops(void)
276 int i;
277 struct vfs_ops *ops;
279 ops = smb_xmalloc(sizeof(struct vfs_ops));
281 for(i=0; i<SMB_VFS_OP_LAST; i++) {
282 ((void**)ops)[i] = vfs_opaque_ops[i].op;
284 return ops;
287 /*******************************************************************
288 Check if directory exists.
289 ********************************************************************/
291 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
293 SMB_STRUCT_STAT st2;
294 BOOL ret;
296 if (!st)
297 st = &st2;
299 if (vfs_stat(conn,dname,st) != 0)
300 return(False);
302 ret = S_ISDIR(st->st_mode);
303 if(!ret)
304 errno = ENOTDIR;
306 return ret;
309 /*******************************************************************
310 vfs getwd wrapper
311 ********************************************************************/
313 static char *vfs_getwd(connection_struct *conn, char *path)
315 return conn->vfs_ops.getwd(conn,path);
318 /*******************************************************************
319 vfs mkdir wrapper
320 ********************************************************************/
322 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
324 int ret;
325 SMB_STRUCT_STAT sbuf;
327 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
329 inherit_access_acl(conn, name, mode);
332 * Check if high bits should have been set,
333 * then (if bits are missing): add them.
334 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
336 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
337 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
338 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
340 return ret;
343 /*******************************************************************
344 Check if an object exists in the vfs.
345 ********************************************************************/
347 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
349 SMB_STRUCT_STAT st;
351 if (!sbuf)
352 sbuf = &st;
354 ZERO_STRUCTP(sbuf);
356 if (vfs_stat(conn,fname,sbuf) == -1)
357 return(False);
358 return True;
361 /*******************************************************************
362 Check if a file exists in the vfs.
363 ********************************************************************/
365 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
367 SMB_STRUCT_STAT st;
369 if (!sbuf)
370 sbuf = &st;
372 ZERO_STRUCTP(sbuf);
374 if (vfs_stat(conn,fname,sbuf) == -1)
375 return False;
376 return(S_ISREG(sbuf->st_mode));
379 /****************************************************************************
380 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
381 ****************************************************************************/
383 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
385 size_t total=0;
387 while (total < byte_count)
389 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
390 byte_count - total);
392 if (ret == 0) return total;
393 if (ret == -1) {
394 if (errno == EINTR)
395 continue;
396 else
397 return -1;
399 total += ret;
401 return (ssize_t)total;
404 /****************************************************************************
405 Write data to a fd on the vfs.
406 ****************************************************************************/
408 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
410 size_t total=0;
411 ssize_t ret;
413 while (total < N) {
414 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
416 if (ret == -1)
417 return -1;
418 if (ret == 0)
419 return total;
421 total += ret;
423 return (ssize_t)total;
426 /****************************************************************************
427 An allocate file space call using the vfs interface.
428 Allocates space for a file from a filedescriptor.
429 Returns 0 on success, -1 on failure.
430 ****************************************************************************/
432 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
434 int ret;
435 SMB_STRUCT_STAT st;
436 connection_struct *conn = fsp->conn;
437 struct vfs_ops *vfs_ops = &conn->vfs_ops;
438 SMB_BIG_UINT space_avail;
439 SMB_BIG_UINT bsize,dfree,dsize;
441 release_level_2_oplocks_on_change(fsp);
444 * Actually try and commit the space on disk....
447 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
449 if (((SMB_OFF_T)len) < 0) {
450 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
451 return -1;
454 ret = vfs_fstat(fsp,fsp->fd,&st);
455 if (ret == -1)
456 return ret;
458 if (len == (SMB_BIG_UINT)st.st_size)
459 return 0;
461 if (len < (SMB_BIG_UINT)st.st_size) {
462 /* Shrink - use ftruncate. */
464 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
465 fsp->fsp_name, (double)st.st_size ));
467 flush_write_cache(fsp, SIZECHANGE_FLUSH);
468 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
469 set_filelen_write_cache(fsp, len);
471 return ret;
474 /* Grow - we need to test if we have enough space. */
476 if (!lp_strict_allocate(SNUM(fsp->conn)))
477 return 0;
479 len -= st.st_size;
480 len /= 1024; /* Len is now number of 1k blocks needed. */
481 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
483 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
484 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
486 if (len > space_avail) {
487 errno = ENOSPC;
488 return -1;
491 return 0;
494 /****************************************************************************
495 A vfs set_filelen call.
496 set the length of a file from a filedescriptor.
497 Returns 0 on success, -1 on failure.
498 ****************************************************************************/
500 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
502 int ret;
504 release_level_2_oplocks_on_change(fsp);
505 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
506 flush_write_cache(fsp, SIZECHANGE_FLUSH);
507 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
508 set_filelen_write_cache(fsp, len);
510 return ret;
513 /****************************************************************************
514 Transfer some data (n bytes) between two file_struct's.
515 ****************************************************************************/
517 static files_struct *in_fsp;
518 static files_struct *out_fsp;
520 static ssize_t read_fn(int fd, void *buf, size_t len)
522 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
525 static ssize_t write_fn(int fd, const void *buf, size_t len)
527 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
530 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
532 in_fsp = in;
533 out_fsp = out;
535 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
538 /*******************************************************************
539 A vfs_readdir wrapper which just returns the file name.
540 ********************************************************************/
542 char *vfs_readdirname(connection_struct *conn, void *p)
544 struct dirent *ptr;
545 char *dname;
547 if (!p)
548 return(NULL);
550 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
551 if (!ptr)
552 return(NULL);
554 dname = ptr->d_name;
556 #ifdef NEXT2
557 if (telldir(p) < 0)
558 return(NULL);
559 #endif
561 #ifdef HAVE_BROKEN_READDIR
562 /* using /usr/ucb/cc is BAD */
563 dname = dname - 2;
564 #endif
566 return(dname);
569 /* VFS options not quite working yet */
571 #if 0
573 /***************************************************************************
574 handle the interpretation of the vfs option parameter
575 *************************************************************************/
576 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
578 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
579 int i;
581 /* Create new vfs option */
583 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
584 if (new_option == NULL) {
585 return False;
588 ZERO_STRUCTP(new_option);
590 /* Get name and value */
592 new_option->name = strtok(pszParmValue, "=");
594 if (new_option->name == NULL) {
595 return False;
598 while(isspace(*new_option->name)) {
599 new_option->name++;
602 for (i = strlen(new_option->name); i > 0; i--) {
603 if (!isspace(new_option->name[i - 1])) break;
606 new_option->name[i] = '\0';
607 new_option->name = strdup(new_option->name);
609 new_option->value = strtok(NULL, "=");
611 if (new_option->value != NULL) {
613 while(isspace(*new_option->value)) {
614 new_option->value++;
617 for (i = strlen(new_option->value); i > 0; i--) {
618 if (!isspace(new_option->value[i - 1])) break;
621 new_option->value[i] = '\0';
622 new_option->value = strdup(new_option->value);
625 /* Add to list */
627 DLIST_ADD(*options, new_option);
629 return True;
632 #endif
635 /*******************************************************************
636 A wrapper for vfs_chdir().
637 ********************************************************************/
639 int vfs_ChDir(connection_struct *conn, const char *path)
641 int res;
642 static pstring LastDir="";
644 if (strcsequal(path,"."))
645 return(0);
647 if (*path == '/' && strcsequal(LastDir,path))
648 return(0);
650 DEBUG(3,("vfs_ChDir to %s\n",path));
652 res = vfs_chdir(conn,path);
653 if (!res)
654 pstrcpy(LastDir,path);
655 return(res);
658 /* number of list structures for a caching GetWd function. */
659 #define MAX_GETWDCACHE (50)
661 static struct {
662 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
663 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
664 char *dos_path; /* The pathname in DOS format. */
665 BOOL valid;
666 } ino_list[MAX_GETWDCACHE];
668 extern BOOL use_getwd_cache;
670 /****************************************************************************
671 Prompte a ptr (to make it recently used)
672 ****************************************************************************/
674 static void array_promote(char *array,int elsize,int element)
676 char *p;
677 if (element == 0)
678 return;
680 p = (char *)malloc(elsize);
682 if (!p) {
683 DEBUG(5,("array_promote: malloc fail\n"));
684 return;
687 memcpy(p,array + element * elsize, elsize);
688 memmove(array + elsize,array,elsize*element);
689 memcpy(array,p,elsize);
690 SAFE_FREE(p);
693 /*******************************************************************
694 Return the absolute current directory path - given a UNIX pathname.
695 Note that this path is returned in DOS format, not UNIX
696 format. Note this can be called with conn == NULL.
697 ********************************************************************/
699 char *vfs_GetWd(connection_struct *conn, char *path)
701 pstring s;
702 static BOOL getwd_cache_init = False;
703 SMB_STRUCT_STAT st, st2;
704 int i;
706 *s = 0;
708 if (!use_getwd_cache)
709 return(vfs_getwd(conn,path));
711 /* init the cache */
712 if (!getwd_cache_init) {
713 getwd_cache_init = True;
714 for (i=0;i<MAX_GETWDCACHE;i++) {
715 string_set(&ino_list[i].dos_path,"");
716 ino_list[i].valid = False;
720 /* Get the inode of the current directory, if this doesn't work we're
721 in trouble :-) */
723 if (vfs_stat(conn, ".",&st) == -1) {
724 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
725 return(vfs_getwd(conn,path));
729 for (i=0; i<MAX_GETWDCACHE; i++) {
730 if (ino_list[i].valid) {
732 /* If we have found an entry with a matching inode and dev number
733 then find the inode number for the directory in the cached string.
734 If this agrees with that returned by the stat for the current
735 directory then all is o.k. (but make sure it is a directory all
736 the same...) */
738 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
739 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
740 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
741 (st2.st_mode & S_IFMT) == S_IFDIR) {
742 pstrcpy (path, ino_list[i].dos_path);
744 /* promote it for future use */
745 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
746 return (path);
747 } else {
748 /* If the inode is different then something's changed,
749 scrub the entry and start from scratch. */
750 ino_list[i].valid = False;
757 /* We don't have the information to hand so rely on traditional methods.
758 The very slow getcwd, which spawns a process on some systems, or the
759 not quite so bad getwd. */
761 if (!vfs_getwd(conn,s)) {
762 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
763 return (NULL);
766 pstrcpy(path,s);
768 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
770 /* add it to the cache */
771 i = MAX_GETWDCACHE - 1;
772 string_set(&ino_list[i].dos_path,s);
773 ino_list[i].dev = st.st_dev;
774 ino_list[i].inode = st.st_ino;
775 ino_list[i].valid = True;
777 /* put it at the top of the list */
778 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
780 return (path);
784 /* check if the file 'nmae' is a symlink, in that case check that it point to
785 a file that reside under the 'dir' tree */
787 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
789 BOOL ret = True;
790 pstring flink;
791 pstring cleanlink;
792 pstring savedir;
793 pstring realdir;
794 size_t reallen;
796 if (!vfs_GetWd(conn, savedir)) {
797 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
798 return False;
801 if (vfs_ChDir(conn, dir) != 0) {
802 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
803 return False;
806 if (!vfs_GetWd(conn, realdir)) {
807 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
808 vfs_ChDir(conn, savedir);
809 return(False);
812 reallen = strlen(realdir);
813 if (realdir[reallen -1] == '/') {
814 reallen--;
815 realdir[reallen] = 0;
818 if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
819 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
820 if (*flink == '/') {
821 pstrcpy(cleanlink, flink);
822 } else {
823 pstrcpy(cleanlink, realdir);
824 pstrcat(cleanlink, "/");
825 pstrcat(cleanlink, flink);
827 unix_clean_name(cleanlink);
829 if (strncmp(cleanlink, realdir, reallen) != 0) {
830 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
831 ret = False;
835 vfs_ChDir(conn, savedir);
837 return ret;
840 /*******************************************************************
841 Reduce a file name, removing .. elements and checking that
842 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
843 on the system that has the referenced file system.
844 Widelinks are allowed if widelinks is true.
845 ********************************************************************/
847 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
849 #ifndef REDUCE_PATHS
850 return True;
851 #else
852 pstring dir2;
853 pstring wd;
854 pstring base_name;
855 pstring newname;
856 char *p=NULL;
857 BOOL relative = (*s != '/');
859 *dir2 = *wd = *base_name = *newname = 0;
861 if (widelinks) {
862 unix_clean_name(s);
863 /* can't have a leading .. */
864 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
865 DEBUG(3,("Illegal file name? (%s)\n",s));
866 return(False);
869 if (strlen(s) == 0)
870 pstrcpy(s,"./");
872 return(True);
875 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
877 /* remove any double slashes */
878 all_string_sub(s,"//","/",0);
880 pstrcpy(base_name,s);
881 p = strrchr_m(base_name,'/');
883 if (!p)
884 return readlink_check(conn, dir, s);
886 if (!vfs_GetWd(conn,wd)) {
887 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
888 return(False);
891 if (vfs_ChDir(conn,dir) != 0) {
892 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
893 return(False);
896 if (!vfs_GetWd(conn,dir2)) {
897 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
898 vfs_ChDir(conn,wd);
899 return(False);
902 if (p && (p != base_name)) {
903 *p = 0;
904 if (strcmp(p+1,".")==0)
905 p[1]=0;
906 if (strcmp(p+1,"..")==0)
907 *p = '/';
910 if (vfs_ChDir(conn,base_name) != 0) {
911 vfs_ChDir(conn,wd);
912 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
913 return(False);
916 if (!vfs_GetWd(conn,newname)) {
917 vfs_ChDir(conn,wd);
918 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
919 return(False);
922 if (p && (p != base_name)) {
923 pstrcat(newname,"/");
924 pstrcat(newname,p+1);
928 size_t l = strlen(dir2);
929 if (dir2[l-1] == '/')
930 l--;
932 if (strncmp(newname,dir2,l) != 0) {
933 vfs_ChDir(conn,wd);
934 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
935 return(False);
938 if (!readlink_check(conn, dir, newname)) {
939 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
940 return(False);
943 if (relative) {
944 if (newname[l] == '/')
945 pstrcpy(s,newname + l + 1);
946 else
947 pstrcpy(s,newname+l);
948 } else
949 pstrcpy(s,newname);
952 vfs_ChDir(conn,wd);
954 if (strlen(s) == 0)
955 pstrcpy(s,"./");
957 DEBUG(3,("reduced to %s\n",s));
958 return(True);
959 #endif