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_BIG_UINT len)
430 connection_struct *conn = fsp->conn;
431 struct vfs_ops *vfs_ops = &conn->vfs_ops;
432 SMB_BIG_UINT 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 if (((SMB_OFF_T)len) < 0) {
444 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
448 ret = vfs_fstat(fsp,fsp->fd,&st);
452 if (len == (SMB_BIG_UINT)st.st_size)
455 if (len < (SMB_BIG_UINT)st.st_size) {
456 /* Shrink - use ftruncate. */
458 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
459 fsp->fsp_name, (double)st.st_size ));
461 flush_write_cache(fsp, SIZECHANGE_FLUSH);
462 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
463 set_filelen_write_cache(fsp, len);
468 /* Grow - we need to test if we have enough space. */
470 if (!lp_strict_allocate(SNUM(fsp->conn)))
474 len /= 1024; /* Len is now number of 1k blocks needed. */
475 space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
477 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
478 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
480 if (len > space_avail) {
488 /****************************************************************************
489 A vfs set_filelen call.
490 set the length of a file from a filedescriptor.
491 Returns 0 on success, -1 on failure.
492 ****************************************************************************/
494 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
498 release_level_2_oplocks_on_change(fsp);
499 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
500 flush_write_cache(fsp, SIZECHANGE_FLUSH);
501 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
502 set_filelen_write_cache(fsp, len);
507 /****************************************************************************
508 Transfer some data (n bytes) between two file_struct's.
509 ****************************************************************************/
511 static files_struct *in_fsp;
512 static files_struct *out_fsp;
514 static ssize_t read_fn(int fd, void *buf, size_t len)
516 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
519 static ssize_t write_fn(int fd, const void *buf, size_t len)
521 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
524 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
529 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
532 /*******************************************************************
533 A vfs_readdir wrapper which just returns the file name.
534 ********************************************************************/
536 char *vfs_readdirname(connection_struct *conn, void *p)
544 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
555 #ifdef HAVE_BROKEN_READDIR
556 /* using /usr/ucb/cc is BAD */
563 /* VFS options not quite working yet */
567 /***************************************************************************
568 handle the interpretation of the vfs option parameter
569 *************************************************************************/
570 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
572 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
575 /* Create new vfs option */
577 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
578 if (new_option == NULL) {
582 ZERO_STRUCTP(new_option);
584 /* Get name and value */
586 new_option->name = strtok(pszParmValue, "=");
588 if (new_option->name == NULL) {
592 while(isspace(*new_option->name)) {
596 for (i = strlen(new_option->name); i > 0; i--) {
597 if (!isspace(new_option->name[i - 1])) break;
600 new_option->name[i] = '\0';
601 new_option->name = strdup(new_option->name);
603 new_option->value = strtok(NULL, "=");
605 if (new_option->value != NULL) {
607 while(isspace(*new_option->value)) {
611 for (i = strlen(new_option->value); i > 0; i--) {
612 if (!isspace(new_option->value[i - 1])) break;
615 new_option->value[i] = '\0';
616 new_option->value = strdup(new_option->value);
621 DLIST_ADD(*options, new_option);
629 /*******************************************************************
630 A wrapper for vfs_chdir().
631 ********************************************************************/
633 int vfs_ChDir(connection_struct *conn, char *path)
636 static pstring LastDir="";
638 if (strcsequal(path,"."))
641 if (*path == '/' && strcsequal(LastDir,path))
644 DEBUG(3,("vfs_ChDir to %s\n",path));
646 res = vfs_chdir(conn,path);
648 pstrcpy(LastDir,path);
652 /* number of list structures for a caching GetWd function. */
653 #define MAX_GETWDCACHE (50)
656 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
657 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
658 char *dos_path; /* The pathname in DOS format. */
660 } ino_list[MAX_GETWDCACHE];
662 extern BOOL use_getwd_cache;
664 /****************************************************************************
665 Prompte a ptr (to make it recently used)
666 ****************************************************************************/
668 static void array_promote(char *array,int elsize,int element)
674 p = (char *)malloc(elsize);
677 DEBUG(5,("array_promote: malloc fail\n"));
681 memcpy(p,array + element * elsize, elsize);
682 memmove(array + elsize,array,elsize*element);
683 memcpy(array,p,elsize);
687 /*******************************************************************
688 Return the absolute current directory path - given a UNIX pathname.
689 Note that this path is returned in DOS format, not UNIX
690 format. Note this can be called with conn == NULL.
691 ********************************************************************/
693 char *vfs_GetWd(connection_struct *conn, char *path)
696 static BOOL getwd_cache_init = False;
697 SMB_STRUCT_STAT st, st2;
702 if (!use_getwd_cache)
703 return(vfs_getwd(conn,path));
706 if (!getwd_cache_init) {
707 getwd_cache_init = True;
708 for (i=0;i<MAX_GETWDCACHE;i++) {
709 string_set(&ino_list[i].dos_path,"");
710 ino_list[i].valid = False;
714 /* Get the inode of the current directory, if this doesn't work we're
717 if (vfs_stat(conn, ".",&st) == -1) {
718 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
719 return(vfs_getwd(conn,path));
723 for (i=0; i<MAX_GETWDCACHE; i++) {
724 if (ino_list[i].valid) {
726 /* If we have found an entry with a matching inode and dev number
727 then find the inode number for the directory in the cached string.
728 If this agrees with that returned by the stat for the current
729 directory then all is o.k. (but make sure it is a directory all
732 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
733 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
734 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
735 (st2.st_mode & S_IFMT) == S_IFDIR) {
736 pstrcpy (path, ino_list[i].dos_path);
738 /* promote it for future use */
739 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
742 /* If the inode is different then something's changed,
743 scrub the entry and start from scratch. */
744 ino_list[i].valid = False;
751 /* We don't have the information to hand so rely on traditional methods.
752 The very slow getcwd, which spawns a process on some systems, or the
753 not quite so bad getwd. */
755 if (!vfs_getwd(conn,s)) {
756 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
762 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
764 /* add it to the cache */
765 i = MAX_GETWDCACHE - 1;
766 string_set(&ino_list[i].dos_path,s);
767 ino_list[i].dev = st.st_dev;
768 ino_list[i].inode = st.st_ino;
769 ino_list[i].valid = True;
771 /* put it at the top of the list */
772 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
777 /*******************************************************************
778 Reduce a file name, removing .. elements and checking that
779 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
780 on the system that has the referenced file system.
781 Widelinks are allowed if widelinks is true.
782 ********************************************************************/
784 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
794 BOOL relative = (*s != '/');
796 *dir2 = *wd = *base_name = *newname = 0;
800 /* can't have a leading .. */
801 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
802 DEBUG(3,("Illegal file name? (%s)\n",s));
812 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
814 /* remove any double slashes */
815 all_string_sub(s,"//","/",0);
817 pstrcpy(base_name,s);
818 p = strrchr_m(base_name,'/');
823 if (!vfs_GetWd(conn,wd)) {
824 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
828 if (vfs_ChDir(conn,dir) != 0) {
829 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
833 if (!vfs_GetWd(conn,dir2)) {
834 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
839 if (p && (p != base_name)) {
841 if (strcmp(p+1,".")==0)
843 if (strcmp(p+1,"..")==0)
847 if (vfs_ChDir(conn,base_name) != 0) {
849 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
853 if (!vfs_GetWd(conn,newname)) {
855 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
859 if (p && (p != base_name)) {
860 pstrcat(newname,"/");
861 pstrcat(newname,p+1);
865 size_t l = strlen(dir2);
866 if (dir2[l-1] == '/')
869 if (strncmp(newname,dir2,l) != 0) {
871 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
876 if (newname[l] == '/')
877 pstrcpy(s,newname + l + 1);
879 pstrcpy(s,newname+l);
889 DEBUG(3,("reduced to %s\n",s));