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
31 /* Some structures to help us initialise the vfs operations table */
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 = {
52 vfswrap_dummy_connect,
53 vfswrap_dummy_disconnect,
56 /* Directory operations */
98 /* POSIX ACL operations. */
99 #if defined(HAVE_NO_ACLS)
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 *);
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()));
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));
170 /* Initialise vfs_ops structure */
172 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
173 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
177 if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
178 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
179 vfs_version, SMB_VFS_INTERFACE_VERSION ));
183 if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
184 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
185 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
186 vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
190 for(i=0; ops[i].op != NULL; i++) {
191 DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
192 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
193 /* Check whether this operation was already made opaque by different module */
194 if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
195 /* No, it isn't overloaded yet. Overload. */
196 DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
197 vfs_opaque_ops[ops[i].type] = ops[i];
200 /* Change current VFS disposition*/
201 DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
202 ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
208 /*****************************************************************
210 ******************************************************************/
212 BOOL smbd_vfs_init(connection_struct *conn)
214 char **vfs_objects, *vfsobj, *vfs_module, *vfs_path;
216 struct smb_vfs_handle_struct *handle;
218 /* Normal share - initialise with disk access functions */
219 vfs_init_default(conn);
221 /* Override VFS functions if 'vfs object' was specified*/
222 if (*lp_vfsobj(SNUM(conn))) {
224 for(i=0; i<SMB_VFS_OP_LAST; i++) {
225 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
226 vfs_opaque_ops[i].type = i;
227 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
229 if (string_set(&vfsobj, lp_vfsobj(SNUM(conn)))) {
230 /* Parse passed modules specification to array of modules */
231 set_first_token(vfsobj);
232 /* We are using default separators: ' \t\r\n' */
233 vfs_objects = toktocliplist(&nobj, NULL);
235 vfs_path = lp_vfs_path(SNUM(conn));
236 conn->vfs_private = NULL;
237 for(i=nobj-1; i>=0; i--) {
238 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
239 /* Loadable object file */
240 handle->handle = NULL;
241 DLIST_ADD(conn->vfs_private, handle)
244 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[i]);
246 asprintf(&vfs_module, "%s", vfs_objects[i]);
248 if (!vfs_init_custom(conn, vfs_module)) {
249 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
250 string_free(&vfsobj);
251 SAFE_FREE(vfs_module);
254 SAFE_FREE(vfs_module);
257 string_free(&vfsobj);
264 /*******************************************************************
265 Create vfs_ops reflecting current vfs_opaque_ops
266 *******************************************************************/
268 struct vfs_ops *smb_vfs_get_opaque_ops(void)
273 ops = smb_xmalloc(sizeof(struct vfs_ops));
275 for(i=0; i<SMB_VFS_OP_LAST; i++) {
276 ((void**)ops)[i] = vfs_opaque_ops[i].op;
281 /*******************************************************************
282 Check if directory exists.
283 ********************************************************************/
285 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
293 if (vfs_stat(conn,dname,st) != 0)
296 ret = S_ISDIR(st->st_mode);
303 /*******************************************************************
305 ********************************************************************/
307 static char *vfs_getwd(connection_struct *conn, char *path)
309 return conn->vfs_ops.getwd(conn,path);
312 /*******************************************************************
314 ********************************************************************/
316 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
319 SMB_STRUCT_STAT sbuf;
321 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
323 inherit_access_acl(conn, name, mode);
326 * Check if high bits should have been set,
327 * then (if bits are missing): add them.
328 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
330 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
331 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
332 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
337 /*******************************************************************
338 Check if an object exists in the vfs.
339 ********************************************************************/
341 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
350 if (vfs_stat(conn,fname,sbuf) == -1)
355 /*******************************************************************
356 Check if a file exists in the vfs.
357 ********************************************************************/
359 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
368 if (vfs_stat(conn,fname,sbuf) == -1)
370 return(S_ISREG(sbuf->st_mode));
373 /****************************************************************************
374 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
375 ****************************************************************************/
377 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
381 while (total < byte_count)
383 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
386 if (ret == 0) return total;
395 return (ssize_t)total;
398 /****************************************************************************
399 Write data to a fd on the vfs.
400 ****************************************************************************/
402 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
408 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
417 return (ssize_t)total;
420 /****************************************************************************
421 An allocate file space call using the vfs interface.
422 Allocates space for a file from a filedescriptor.
423 Returns 0 on success, -1 on failure.
424 ****************************************************************************/
426 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
430 connection_struct *conn = fsp->conn;
431 struct vfs_ops *vfs_ops = &conn->vfs_ops;
432 SMB_OFF_T space_avail;
433 SMB_BIG_UINT bsize,dfree,dsize;
435 release_level_2_oplocks_on_change(fsp);
438 * Actually try and commit the space on disk....
441 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
443 ret = vfs_fstat(fsp,fsp->fd,&st);
447 if (len == st.st_size)
450 if (len < st.st_size) {
451 /* Shrink - use ftruncate. */
453 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
454 fsp->fsp_name, (double)st.st_size ));
456 flush_write_cache(fsp, SIZECHANGE_FLUSH);
457 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
458 set_filelen_write_cache(fsp, len);
463 /* Grow - we need to test if we have enough space. */
465 if (!lp_strict_allocate(SNUM(fsp->conn)))
469 len /= 1024; /* Len is now number of 1k blocks needed. */
470 space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
472 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
473 fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
475 if (len > space_avail) {
483 /****************************************************************************
484 A vfs set_filelen call.
485 set the length of a file from a filedescriptor.
486 Returns 0 on success, -1 on failure.
487 ****************************************************************************/
489 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
493 release_level_2_oplocks_on_change(fsp);
494 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
495 flush_write_cache(fsp, SIZECHANGE_FLUSH);
496 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
497 set_filelen_write_cache(fsp, len);
502 /****************************************************************************
503 Transfer some data (n bytes) between two file_struct's.
504 ****************************************************************************/
506 static files_struct *in_fsp;
507 static files_struct *out_fsp;
509 static ssize_t read_fn(int fd, void *buf, size_t len)
511 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
514 static ssize_t write_fn(int fd, const void *buf, size_t len)
516 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
519 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
524 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
527 /*******************************************************************
528 A vfs_readdir wrapper which just returns the file name.
529 ********************************************************************/
531 char *vfs_readdirname(connection_struct *conn, void *p)
539 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
550 #ifdef HAVE_BROKEN_READDIR
551 /* using /usr/ucb/cc is BAD */
558 /* VFS options not quite working yet */
562 /***************************************************************************
563 handle the interpretation of the vfs option parameter
564 *************************************************************************/
565 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
567 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
570 /* Create new vfs option */
572 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
573 if (new_option == NULL) {
577 ZERO_STRUCTP(new_option);
579 /* Get name and value */
581 new_option->name = strtok(pszParmValue, "=");
583 if (new_option->name == NULL) {
587 while(isspace(*new_option->name)) {
591 for (i = strlen(new_option->name); i > 0; i--) {
592 if (!isspace(new_option->name[i - 1])) break;
595 new_option->name[i] = '\0';
596 new_option->name = strdup(new_option->name);
598 new_option->value = strtok(NULL, "=");
600 if (new_option->value != NULL) {
602 while(isspace(*new_option->value)) {
606 for (i = strlen(new_option->value); i > 0; i--) {
607 if (!isspace(new_option->value[i - 1])) break;
610 new_option->value[i] = '\0';
611 new_option->value = strdup(new_option->value);
616 DLIST_ADD(*options, new_option);
624 /*******************************************************************
625 A wrapper for vfs_chdir().
626 ********************************************************************/
628 int vfs_ChDir(connection_struct *conn, char *path)
631 static pstring LastDir="";
633 if (strcsequal(path,"."))
636 if (*path == '/' && strcsequal(LastDir,path))
639 DEBUG(3,("vfs_ChDir to %s\n",path));
641 res = vfs_chdir(conn,path);
643 pstrcpy(LastDir,path);
647 /* number of list structures for a caching GetWd function. */
648 #define MAX_GETWDCACHE (50)
651 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
652 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
653 char *dos_path; /* The pathname in DOS format. */
655 } ino_list[MAX_GETWDCACHE];
657 extern BOOL use_getwd_cache;
659 /****************************************************************************
660 Prompte a ptr (to make it recently used)
661 ****************************************************************************/
663 static void array_promote(char *array,int elsize,int element)
669 p = (char *)malloc(elsize);
672 DEBUG(5,("array_promote: malloc fail\n"));
676 memcpy(p,array + element * elsize, elsize);
677 memmove(array + elsize,array,elsize*element);
678 memcpy(array,p,elsize);
682 /*******************************************************************
683 Return the absolute current directory path - given a UNIX pathname.
684 Note that this path is returned in DOS format, not UNIX
685 format. Note this can be called with conn == NULL.
686 ********************************************************************/
688 char *vfs_GetWd(connection_struct *conn, char *path)
691 static BOOL getwd_cache_init = False;
692 SMB_STRUCT_STAT st, st2;
697 if (!use_getwd_cache)
698 return(vfs_getwd(conn,path));
701 if (!getwd_cache_init) {
702 getwd_cache_init = True;
703 for (i=0;i<MAX_GETWDCACHE;i++) {
704 string_set(&ino_list[i].dos_path,"");
705 ino_list[i].valid = False;
709 /* Get the inode of the current directory, if this doesn't work we're
712 if (vfs_stat(conn, ".",&st) == -1) {
713 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
714 return(vfs_getwd(conn,path));
718 for (i=0; i<MAX_GETWDCACHE; i++) {
719 if (ino_list[i].valid) {
721 /* If we have found an entry with a matching inode and dev number
722 then find the inode number for the directory in the cached string.
723 If this agrees with that returned by the stat for the current
724 directory then all is o.k. (but make sure it is a directory all
727 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
728 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
729 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
730 (st2.st_mode & S_IFMT) == S_IFDIR) {
731 pstrcpy (path, ino_list[i].dos_path);
733 /* promote it for future use */
734 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
737 /* If the inode is different then something's changed,
738 scrub the entry and start from scratch. */
739 ino_list[i].valid = False;
746 /* We don't have the information to hand so rely on traditional methods.
747 The very slow getcwd, which spawns a process on some systems, or the
748 not quite so bad getwd. */
750 if (!vfs_getwd(conn,s)) {
751 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
757 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
759 /* add it to the cache */
760 i = MAX_GETWDCACHE - 1;
761 string_set(&ino_list[i].dos_path,s);
762 ino_list[i].dev = st.st_dev;
763 ino_list[i].inode = st.st_ino;
764 ino_list[i].valid = True;
766 /* put it at the top of the list */
767 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
772 /*******************************************************************
773 Reduce a file name, removing .. elements and checking that
774 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
775 on the system that has the referenced file system.
776 Widelinks are allowed if widelinks is true.
777 ********************************************************************/
779 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
789 BOOL relative = (*s != '/');
791 *dir2 = *wd = *base_name = *newname = 0;
795 /* can't have a leading .. */
796 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
797 DEBUG(3,("Illegal file name? (%s)\n",s));
807 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
809 /* remove any double slashes */
810 all_string_sub(s,"//","/",0);
812 pstrcpy(base_name,s);
813 p = strrchr_m(base_name,'/');
818 if (!vfs_GetWd(conn,wd)) {
819 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
823 if (vfs_ChDir(conn,dir) != 0) {
824 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
828 if (!vfs_GetWd(conn,dir2)) {
829 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
834 if (p && (p != base_name)) {
836 if (strcmp(p+1,".")==0)
838 if (strcmp(p+1,"..")==0)
842 if (vfs_ChDir(conn,base_name) != 0) {
844 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
848 if (!vfs_GetWd(conn,newname)) {
850 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
854 if (p && (p != base_name)) {
855 pstrcat(newname,"/");
856 pstrcat(newname,p+1);
860 size_t l = strlen(dir2);
861 if (dir2[l-1] == '/')
864 if (strncmp(newname,dir2,l) != 0) {
866 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
871 if (newname[l] == '/')
872 pstrcpy(s,newname + l + 1);
874 pstrcpy(s,newname+l);
884 DEBUG(3,("reduced to %s\n",s));