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 * Check if high bits should have been set,
324 * then (if bits are missing): add them.
325 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
327 if(mode
& ~(S_IRWXU
|S_IRWXG
|S_IRWXO
) &&
328 !vfs_stat(conn
,name
,&sbuf
) && (mode
& ~sbuf
.st_mode
))
329 vfs_chmod(conn
,name
,sbuf
.st_mode
| (mode
& ~sbuf
.st_mode
));
334 /*******************************************************************
335 Check if an object exists in the vfs.
336 ********************************************************************/
338 BOOL
vfs_object_exist(connection_struct
*conn
,const char *fname
,SMB_STRUCT_STAT
*sbuf
)
347 if (vfs_stat(conn
,fname
,sbuf
) == -1)
352 /*******************************************************************
353 Check if a file exists in the vfs.
354 ********************************************************************/
356 BOOL
vfs_file_exist(connection_struct
*conn
, const char *fname
,SMB_STRUCT_STAT
*sbuf
)
365 if (vfs_stat(conn
,fname
,sbuf
) == -1)
367 return(S_ISREG(sbuf
->st_mode
));
370 /****************************************************************************
371 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
372 ****************************************************************************/
374 ssize_t
vfs_read_data(files_struct
*fsp
, char *buf
, size_t byte_count
)
378 while (total
< byte_count
)
380 ssize_t ret
= fsp
->conn
->vfs_ops
.read(fsp
, fsp
->fd
, buf
+ total
,
383 if (ret
== 0) return total
;
392 return (ssize_t
)total
;
395 /****************************************************************************
396 Write data to a fd on the vfs.
397 ****************************************************************************/
399 ssize_t
vfs_write_data(files_struct
*fsp
,const char *buffer
,size_t N
)
405 ret
= fsp
->conn
->vfs_ops
.write(fsp
,fsp
->fd
,buffer
+ total
,N
- total
);
414 return (ssize_t
)total
;
417 /****************************************************************************
418 An allocate file space call using the vfs interface.
419 Allocates space for a file from a filedescriptor.
420 Returns 0 on success, -1 on failure.
421 ****************************************************************************/
423 int vfs_allocate_file_space(files_struct
*fsp
, SMB_OFF_T len
)
427 connection_struct
*conn
= fsp
->conn
;
428 struct vfs_ops
*vfs_ops
= &conn
->vfs_ops
;
429 SMB_OFF_T space_avail
;
430 SMB_BIG_UINT bsize
,dfree
,dsize
;
432 release_level_2_oplocks_on_change(fsp
);
435 * Actually try and commit the space on disk....
438 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp
->fsp_name
, (double)len
));
440 ret
= vfs_fstat(fsp
,fsp
->fd
,&st
);
444 if (len
== st
.st_size
)
447 if (len
< st
.st_size
) {
448 /* Shrink - use ftruncate. */
450 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
451 fsp
->fsp_name
, (double)st
.st_size
));
453 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
454 if ((ret
= vfs_ops
->ftruncate(fsp
, fsp
->fd
, len
)) != -1) {
455 set_filelen_write_cache(fsp
, len
);
460 /* Grow - we need to test if we have enough space. */
462 if (!lp_strict_allocate(SNUM(fsp
->conn
)))
466 len
/= 1024; /* Len is now number of 1k blocks needed. */
467 space_avail
= (SMB_OFF_T
)conn
->vfs_ops
.disk_free(conn
,fsp
->fsp_name
,False
,&bsize
,&dfree
,&dsize
);
469 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
470 fsp
->fsp_name
, (double)st
.st_size
, (unsigned long)len
, (unsigned long)space_avail
));
472 if (len
> space_avail
) {
480 /****************************************************************************
481 A vfs set_filelen call.
482 set the length of a file from a filedescriptor.
483 Returns 0 on success, -1 on failure.
484 ****************************************************************************/
486 int vfs_set_filelen(files_struct
*fsp
, SMB_OFF_T len
)
490 release_level_2_oplocks_on_change(fsp
);
491 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp
->fsp_name
, (double)len
));
492 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
493 if ((ret
= fsp
->conn
->vfs_ops
.ftruncate(fsp
, fsp
->fd
, len
)) != -1)
494 set_filelen_write_cache(fsp
, len
);
499 /****************************************************************************
500 Transfer some data (n bytes) between two file_struct's.
501 ****************************************************************************/
503 static files_struct
*in_fsp
;
504 static files_struct
*out_fsp
;
506 static ssize_t
read_fn(int fd
, void *buf
, size_t len
)
508 return in_fsp
->conn
->vfs_ops
.read(in_fsp
, fd
, buf
, len
);
511 static ssize_t
write_fn(int fd
, const void *buf
, size_t len
)
513 return out_fsp
->conn
->vfs_ops
.write(out_fsp
, fd
, buf
, len
);
516 SMB_OFF_T
vfs_transfer_file(files_struct
*in
, files_struct
*out
, SMB_OFF_T n
)
521 return transfer_file_internal(in_fsp
->fd
, out_fsp
->fd
, n
, read_fn
, write_fn
);
524 /*******************************************************************
525 A vfs_readdir wrapper which just returns the file name.
526 ********************************************************************/
528 char *vfs_readdirname(connection_struct
*conn
, void *p
)
536 ptr
= (struct dirent
*)conn
->vfs_ops
.readdir(conn
,p
);
547 #ifdef HAVE_BROKEN_READDIR
548 /* using /usr/ucb/cc is BAD */
555 /* VFS options not quite working yet */
559 /***************************************************************************
560 handle the interpretation of the vfs option parameter
561 *************************************************************************/
562 static BOOL
handle_vfs_option(char *pszParmValue
, char **ptr
)
564 struct vfs_options
*new_option
, **options
= (struct vfs_options
**)ptr
;
567 /* Create new vfs option */
569 new_option
= (struct vfs_options
*)malloc(sizeof(*new_option
));
570 if (new_option
== NULL
) {
574 ZERO_STRUCTP(new_option
);
576 /* Get name and value */
578 new_option
->name
= strtok(pszParmValue
, "=");
580 if (new_option
->name
== NULL
) {
584 while(isspace(*new_option
->name
)) {
588 for (i
= strlen(new_option
->name
); i
> 0; i
--) {
589 if (!isspace(new_option
->name
[i
- 1])) break;
592 new_option
->name
[i
] = '\0';
593 new_option
->name
= strdup(new_option
->name
);
595 new_option
->value
= strtok(NULL
, "=");
597 if (new_option
->value
!= NULL
) {
599 while(isspace(*new_option
->value
)) {
603 for (i
= strlen(new_option
->value
); i
> 0; i
--) {
604 if (!isspace(new_option
->value
[i
- 1])) break;
607 new_option
->value
[i
] = '\0';
608 new_option
->value
= strdup(new_option
->value
);
613 DLIST_ADD(*options
, new_option
);
621 /*******************************************************************
622 A wrapper for vfs_chdir().
623 ********************************************************************/
625 int vfs_ChDir(connection_struct
*conn
, char *path
)
628 static pstring LastDir
="";
630 if (strcsequal(path
,"."))
633 if (*path
== '/' && strcsequal(LastDir
,path
))
636 DEBUG(3,("vfs_ChDir to %s\n",path
));
638 res
= vfs_chdir(conn
,path
);
640 pstrcpy(LastDir
,path
);
644 /* number of list structures for a caching GetWd function. */
645 #define MAX_GETWDCACHE (50)
648 SMB_DEV_T dev
; /* These *must* be compatible with the types returned in a stat() call. */
649 SMB_INO_T inode
; /* These *must* be compatible with the types returned in a stat() call. */
650 char *dos_path
; /* The pathname in DOS format. */
652 } ino_list
[MAX_GETWDCACHE
];
654 extern BOOL use_getwd_cache
;
656 /****************************************************************************
657 Prompte a ptr (to make it recently used)
658 ****************************************************************************/
660 static void array_promote(char *array
,int elsize
,int element
)
666 p
= (char *)malloc(elsize
);
669 DEBUG(5,("array_promote: malloc fail\n"));
673 memcpy(p
,array
+ element
* elsize
, elsize
);
674 memmove(array
+ elsize
,array
,elsize
*element
);
675 memcpy(array
,p
,elsize
);
679 /*******************************************************************
680 Return the absolute current directory path - given a UNIX pathname.
681 Note that this path is returned in DOS format, not UNIX
682 format. Note this can be called with conn == NULL.
683 ********************************************************************/
685 char *vfs_GetWd(connection_struct
*conn
, char *path
)
688 static BOOL getwd_cache_init
= False
;
689 SMB_STRUCT_STAT st
, st2
;
694 if (!use_getwd_cache
)
695 return(vfs_getwd(conn
,path
));
698 if (!getwd_cache_init
) {
699 getwd_cache_init
= True
;
700 for (i
=0;i
<MAX_GETWDCACHE
;i
++) {
701 string_set(&ino_list
[i
].dos_path
,"");
702 ino_list
[i
].valid
= False
;
706 /* Get the inode of the current directory, if this doesn't work we're
709 if (vfs_stat(conn
, ".",&st
) == -1) {
710 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path
));
711 return(vfs_getwd(conn
,path
));
715 for (i
=0; i
<MAX_GETWDCACHE
; i
++) {
716 if (ino_list
[i
].valid
) {
718 /* If we have found an entry with a matching inode and dev number
719 then find the inode number for the directory in the cached string.
720 If this agrees with that returned by the stat for the current
721 directory then all is o.k. (but make sure it is a directory all
724 if (st
.st_ino
== ino_list
[i
].inode
&& st
.st_dev
== ino_list
[i
].dev
) {
725 if (vfs_stat(conn
,ino_list
[i
].dos_path
,&st2
) == 0) {
726 if (st
.st_ino
== st2
.st_ino
&& st
.st_dev
== st2
.st_dev
&&
727 (st2
.st_mode
& S_IFMT
) == S_IFDIR
) {
728 pstrcpy (path
, ino_list
[i
].dos_path
);
730 /* promote it for future use */
731 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
734 /* If the inode is different then something's changed,
735 scrub the entry and start from scratch. */
736 ino_list
[i
].valid
= False
;
743 /* We don't have the information to hand so rely on traditional methods.
744 The very slow getcwd, which spawns a process on some systems, or the
745 not quite so bad getwd. */
747 if (!vfs_getwd(conn
,s
)) {
748 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno
)));
754 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s
,(double)st
.st_ino
,(double)st
.st_dev
));
756 /* add it to the cache */
757 i
= MAX_GETWDCACHE
- 1;
758 string_set(&ino_list
[i
].dos_path
,s
);
759 ino_list
[i
].dev
= st
.st_dev
;
760 ino_list
[i
].inode
= st
.st_ino
;
761 ino_list
[i
].valid
= True
;
763 /* put it at the top of the list */
764 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
769 /*******************************************************************
770 Reduce a file name, removing .. elements and checking that
771 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
772 on the system that has the referenced file system.
773 Widelinks are allowed if widelinks is true.
774 ********************************************************************/
776 BOOL
reduce_name(connection_struct
*conn
, char *s
,char *dir
,BOOL widelinks
)
786 BOOL relative
= (*s
!= '/');
788 *dir2
= *wd
= *base_name
= *newname
= 0;
792 /* can't have a leading .. */
793 if (strncmp(s
,"..",2) == 0 && (s
[2]==0 || s
[2]=='/')) {
794 DEBUG(3,("Illegal file name? (%s)\n",s
));
804 DEBUG(3,("reduce_name [%s] [%s]\n",s
,dir
));
806 /* remove any double slashes */
807 all_string_sub(s
,"//","/",0);
809 pstrcpy(base_name
,s
);
810 p
= strrchr_m(base_name
,'/');
815 if (!vfs_GetWd(conn
,wd
)) {
816 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s
,dir
));
820 if (vfs_ChDir(conn
,dir
) != 0) {
821 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir
));
825 if (!vfs_GetWd(conn
,dir2
)) {
826 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir
));
831 if (p
&& (p
!= base_name
)) {
833 if (strcmp(p
+1,".")==0)
835 if (strcmp(p
+1,"..")==0)
839 if (vfs_ChDir(conn
,base_name
) != 0) {
841 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s
,dir
,base_name
));
845 if (!vfs_GetWd(conn
,newname
)) {
847 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s
,dir2
));
851 if (p
&& (p
!= base_name
)) {
852 pstrcat(newname
,"/");
853 pstrcat(newname
,p
+1);
857 size_t l
= strlen(dir2
);
858 if (dir2
[l
-1] == '/')
861 if (strncmp(newname
,dir2
,l
) != 0) {
863 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s
,dir2
,newname
,(int)l
));
868 if (newname
[l
] == '/')
869 pstrcpy(s
,newname
+ l
+ 1);
871 pstrcpy(s
,newname
+l
);
881 DEBUG(3,("reduced to %s\n",s
));