2 Unix SMB/Netbios implementation.
4 VFS initialisation and support functions
5 Copyright (C) Tim Potter 1999
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* Some structures to help us initialise the vfs operations table */
31 /* Default vfs hooks. WARNING: The order of these initialisers is
32 very important. They must be in the same order as defined in
33 vfs.h. Change at your own peril. */
35 struct vfs_ops default_vfs_ops
= {
39 vfswrap_dummy_connect
,
40 vfswrap_dummy_disconnect
,
43 /* Directory operations */
85 /* POSIX ACL operations. */
86 #if defined(HAVE_NO_ACLS)
93 vfswrap_sys_acl_get_entry
,
94 vfswrap_sys_acl_get_tag_type
,
95 vfswrap_sys_acl_get_permset
,
96 vfswrap_sys_acl_get_qualifier
,
97 vfswrap_sys_acl_get_file
,
98 vfswrap_sys_acl_get_fd
,
99 vfswrap_sys_acl_clear_perms
,
100 vfswrap_sys_acl_add_perm
,
101 vfswrap_sys_acl_to_text
,
102 vfswrap_sys_acl_init
,
103 vfswrap_sys_acl_create_entry
,
104 vfswrap_sys_acl_set_tag_type
,
105 vfswrap_sys_acl_set_qualifier
,
106 vfswrap_sys_acl_set_permset
,
107 vfswrap_sys_acl_valid
,
108 vfswrap_sys_acl_set_file
,
109 vfswrap_sys_acl_set_fd
,
110 vfswrap_sys_acl_delete_def_file
,
111 vfswrap_sys_acl_get_perm
,
112 vfswrap_sys_acl_free_text
,
113 vfswrap_sys_acl_free_acl
,
114 vfswrap_sys_acl_free_qualifier
117 /****************************************************************************
118 initialise default vfs hooks
119 ****************************************************************************/
121 static BOOL
vfs_init_default(connection_struct
*conn
)
123 DEBUG(3, ("Initialising default vfs hooks\n"));
125 memcpy(&conn
->vfs_ops
, &default_vfs_ops
, sizeof(struct vfs_ops
));
129 /****************************************************************************
130 initialise custom vfs hooks
131 ****************************************************************************/
133 static BOOL
vfs_init_custom(connection_struct
*conn
)
135 int vfs_version
= -1;
136 struct vfs_ops
*ops
, *(*init_fptr
)(int *, struct vfs_ops
*);
138 DEBUG(3, ("Initialising custom vfs hooks from %s\n", lp_vfsobj(SNUM(conn
))));
140 /* Open object file */
142 if ((conn
->dl_handle
= sys_dlopen(lp_vfsobj(SNUM(conn
)), RTLD_NOW
| RTLD_GLOBAL
)) == NULL
) {
143 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn
)), sys_dlerror()));
147 /* Get handle on vfs_init() symbol */
149 init_fptr
= (struct vfs_ops
*(*)(int *, struct vfs_ops
*))sys_dlsym(conn
->dl_handle
, "vfs_init");
151 if (init_fptr
== NULL
) {
152 DEBUG(0, ("No vfs_init() symbol found in %s\n", lp_vfsobj(SNUM(conn
))));
156 /* Initialise vfs_ops structure */
158 conn
->vfs_ops
= default_vfs_ops
;
160 if ((ops
= init_fptr(&vfs_version
, &default_vfs_ops
)) == NULL
) {
161 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn
))));
165 if (vfs_version
!= SMB_VFS_INTERFACE_VERSION
) {
166 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
167 vfs_version
, SMB_VFS_INTERFACE_VERSION
));
171 if (ops
!= &conn
->vfs_ops
) {
172 memcpy(&conn
->vfs_ops
, ops
, sizeof(struct vfs_ops
));
178 /*****************************************************************
180 ******************************************************************/
182 BOOL
smbd_vfs_init(connection_struct
*conn
)
184 if (*lp_vfsobj(SNUM(conn
))) {
185 /* Loadable object file */
187 if (!vfs_init_custom(conn
)) {
188 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
195 /* Normal share - initialise with disk access functions */
197 return vfs_init_default(conn
);
200 /*******************************************************************
201 Check if directory exists.
202 ********************************************************************/
204 BOOL
vfs_directory_exist(connection_struct
*conn
, const char *dname
, SMB_STRUCT_STAT
*st
)
212 if (vfs_stat(conn
,dname
,st
) != 0)
215 ret
= S_ISDIR(st
->st_mode
);
222 /*******************************************************************
223 vfs mkdir wrapper that calls dos_to_unix.
224 ********************************************************************/
226 int vfs_mkdir(connection_struct
*conn
, char *const fname
, mode_t mode
)
230 SMB_STRUCT_STAT sbuf
;
232 pstrcpy(name
,dos_to_unix_static(fname
)); /* paranoia copy */
233 if(!(ret
=conn
->vfs_ops
.mkdir(conn
,name
,mode
))) {
235 * Check if high bits should have been set,
236 * then (if bits are missing): add them.
237 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
239 if(mode
& ~(S_IRWXU
|S_IRWXG
|S_IRWXO
) &&
240 !vfs_stat(conn
,name
,&sbuf
) && (mode
& ~sbuf
.st_mode
))
241 vfs_chmod(conn
,name
,sbuf
.st_mode
| (mode
& ~sbuf
.st_mode
));
246 /*******************************************************************
247 vfs getwd wrapper that calls dos_to_unix.
248 ********************************************************************/
250 char *vfs_getwd(connection_struct
*conn
, char *unix_path
)
253 wd
= conn
->vfs_ops
.getwd(conn
,unix_path
);
259 /*******************************************************************
260 Check if an object exists in the vfs.
261 ********************************************************************/
263 BOOL
vfs_object_exist(connection_struct
*conn
, const char *fname
,SMB_STRUCT_STAT
*sbuf
)
272 if (vfs_stat(conn
,fname
,sbuf
) == -1)
277 /*******************************************************************
278 Check if a file exists in the vfs.
279 ********************************************************************/
281 BOOL
vfs_file_exist(connection_struct
*conn
, const char *fname
,SMB_STRUCT_STAT
*sbuf
)
290 if (vfs_stat(conn
,fname
,sbuf
) == -1)
292 return(S_ISREG(sbuf
->st_mode
));
295 /****************************************************************************
296 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
297 ****************************************************************************/
299 ssize_t
vfs_read_data(files_struct
*fsp
, char *buf
, size_t byte_count
)
303 while (total
< byte_count
)
305 ssize_t ret
= fsp
->conn
->vfs_ops
.read(fsp
, fsp
->fd
, buf
+ total
,
308 if (ret
== 0) return total
;
317 return (ssize_t
)total
;
320 /****************************************************************************
321 Write data to a fd on the vfs.
322 ****************************************************************************/
324 ssize_t
vfs_write_data(files_struct
*fsp
,const char *buffer
,size_t N
)
330 ret
= fsp
->conn
->vfs_ops
.write(fsp
,fsp
->fd
,buffer
+ total
,N
- total
);
340 return (ssize_t
)total
;
343 /****************************************************************************
344 An allocate file space call using the vfs interface.
345 Allocates space for a file from a filedescriptor.
346 Returns 0 on success, -1 on failure.
347 ****************************************************************************/
349 int vfs_allocate_file_space(files_struct
*fsp
, SMB_BIG_UINT len
)
353 connection_struct
*conn
= fsp
->conn
;
354 struct vfs_ops
*vfs_ops
= &conn
->vfs_ops
;
355 SMB_BIG_UINT space_avail
;
356 SMB_BIG_UINT bsize
,dfree
,dsize
;
358 release_level_2_oplocks_on_change(fsp
);
361 * Actually try and commit the space on disk....
364 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp
->fsp_name
, (double)len
));
366 if (((SMB_OFF_T
)len
) < 0) {
367 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp
->fsp_name
));
371 ret
= vfs_fstat(fsp
,fsp
->fd
,&st
);
375 if (len
== (SMB_BIG_UINT
)st
.st_size
)
378 if (len
< (SMB_BIG_UINT
)st
.st_size
) {
379 /* Shrink - use ftruncate. */
381 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
382 fsp
->fsp_name
, (double)st
.st_size
));
384 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
385 if ((ret
= vfs_ops
->ftruncate(fsp
, fsp
->fd
, (SMB_OFF_T
)len
)) != -1) {
386 set_filelen_write_cache(fsp
, len
);
391 /* Grow - we need to test if we have enough space. */
393 if (!lp_strict_allocate(SNUM(fsp
->conn
)))
397 len
/= 1024; /* Len is now number of 1k blocks needed. */
398 space_avail
= conn
->vfs_ops
.disk_free(conn
,fsp
->fsp_name
,False
,&bsize
,&dfree
,&dsize
);
400 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
401 fsp
->fsp_name
, (double)st
.st_size
, (double)len
, (double)space_avail
));
403 if (len
> space_avail
) {
411 /****************************************************************************
412 A vfs set_filelen call.
413 set the length of a file from a filedescriptor.
414 Returns 0 on success, -1 on failure.
415 ****************************************************************************/
417 int vfs_set_filelen(files_struct
*fsp
, SMB_OFF_T len
)
421 release_level_2_oplocks_on_change(fsp
);
422 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp
->fsp_name
, (double)len
));
423 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
424 if ((ret
= fsp
->conn
->vfs_ops
.ftruncate(fsp
, fsp
->fd
, len
)) != -1)
425 set_filelen_write_cache(fsp
, len
);
430 /****************************************************************************
431 Transfer some data (n bytes) between two file_struct's.
432 ****************************************************************************/
434 static files_struct
*in_fsp
;
435 static files_struct
*out_fsp
;
437 static ssize_t
read_fn(int fd
, void *buf
, size_t len
)
439 return in_fsp
->conn
->vfs_ops
.read(in_fsp
, fd
, buf
, len
);
442 static ssize_t
write_fn(int fd
, const void *buf
, size_t len
)
444 return out_fsp
->conn
->vfs_ops
.write(out_fsp
, fd
, buf
, len
);
447 SMB_OFF_T
vfs_transfer_file(files_struct
*in
, files_struct
*out
, SMB_OFF_T n
)
452 return transfer_file_internal(in_fsp
->fd
, out_fsp
->fd
, n
, read_fn
, write_fn
);
455 /*******************************************************************
456 A vfs_readdir wrapper which just returns the file name.
457 ********************************************************************/
459 char *vfs_readdirname(connection_struct
*conn
, void *p
)
467 ptr
= (struct dirent
*)conn
->vfs_ops
.readdir(conn
,p
);
478 #ifdef HAVE_BROKEN_READDIR
479 /* using /usr/ucb/cc is BAD */
485 memcpy(buf
, dname
, NAMLEN(ptr
)+1);
493 /* VFS options not quite working yet */
497 /***************************************************************************
498 handle the interpretation of the vfs option parameter
499 *************************************************************************/
500 static BOOL
handle_vfs_option(char *pszParmValue
, char **ptr
)
502 struct vfs_options
*new_option
, **options
= (struct vfs_options
**)ptr
;
505 /* Create new vfs option */
507 new_option
= (struct vfs_options
*)malloc(sizeof(*new_option
));
508 if (new_option
== NULL
) {
512 ZERO_STRUCTP(new_option
);
514 /* Get name and value */
516 new_option
->name
= strtok(pszParmValue
, "=");
518 if (new_option
->name
== NULL
) {
522 while(isspace(*new_option
->name
)) {
526 for (i
= strlen(new_option
->name
); i
> 0; i
--) {
527 if (!isspace(new_option
->name
[i
- 1])) break;
530 new_option
->name
[i
] = '\0';
531 new_option
->name
= strdup(new_option
->name
);
533 new_option
->value
= strtok(NULL
, "=");
535 if (new_option
->value
!= NULL
) {
537 while(isspace(*new_option
->value
)) {
541 for (i
= strlen(new_option
->value
); i
> 0; i
--) {
542 if (!isspace(new_option
->value
[i
- 1])) break;
545 new_option
->value
[i
] = '\0';
546 new_option
->value
= strdup(new_option
->value
);
551 DLIST_ADD(*options
, new_option
);
559 /*******************************************************************
560 A wrapper for vfs_chdir().
561 ********************************************************************/
563 int vfs_ChDir(connection_struct
*conn
, const char *path
)
566 static pstring LastDir
="";
568 if (strcsequal(path
,"."))
571 if (*path
== '/' && strcsequal(LastDir
,path
))
574 DEBUG(3,("vfs_ChDir to %s\n",path
));
576 res
= vfs_chdir(conn
,path
);
578 pstrcpy(LastDir
,path
);
582 /* number of list structures for a caching GetWd function. */
583 #define MAX_GETWDCACHE (50)
586 SMB_DEV_T dev
; /* These *must* be compatible with the types returned in a stat() call. */
587 SMB_INO_T inode
; /* These *must* be compatible with the types returned in a stat() call. */
588 char *dos_path
; /* The pathname in DOS format. */
590 } ino_list
[MAX_GETWDCACHE
];
592 extern BOOL use_getwd_cache
;
594 /****************************************************************************
595 Prompte a ptr (to make it recently used)
596 ****************************************************************************/
598 static void array_promote(char *array
,int elsize
,int element
)
604 p
= (char *)malloc(elsize
);
607 DEBUG(5,("array_promote: malloc fail\n"));
611 memcpy(p
,array
+ element
* elsize
, elsize
);
612 memmove(array
+ elsize
,array
,elsize
*element
);
613 memcpy(array
,p
,elsize
);
617 /*******************************************************************
618 Return the absolute current directory path - given a UNIX pathname.
619 Note that this path is returned in DOS format, not UNIX
620 format. Note this can be called with conn == NULL.
621 ********************************************************************/
623 char *vfs_GetWd(connection_struct
*conn
, char *path
)
626 static BOOL getwd_cache_init
= False
;
627 SMB_STRUCT_STAT st
, st2
;
632 if (!use_getwd_cache
)
633 return(vfs_getwd(conn
,path
));
636 if (!getwd_cache_init
) {
637 getwd_cache_init
= True
;
638 for (i
=0;i
<MAX_GETWDCACHE
;i
++) {
639 string_set(&ino_list
[i
].dos_path
,"");
640 ino_list
[i
].valid
= False
;
644 /* Get the inode of the current directory, if this doesn't work we're
647 if (vfs_stat(conn
, ".",&st
) == -1) {
648 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path
));
649 return(vfs_getwd(conn
,path
));
653 for (i
=0; i
<MAX_GETWDCACHE
; i
++) {
654 if (ino_list
[i
].valid
) {
656 /* If we have found an entry with a matching inode and dev number
657 then find the inode number for the directory in the cached string.
658 If this agrees with that returned by the stat for the current
659 directory then all is o.k. (but make sure it is a directory all
662 if (st
.st_ino
== ino_list
[i
].inode
&& st
.st_dev
== ino_list
[i
].dev
) {
663 if (vfs_stat(conn
,ino_list
[i
].dos_path
,&st2
) == 0) {
664 if (st
.st_ino
== st2
.st_ino
&& st
.st_dev
== st2
.st_dev
&&
665 (st2
.st_mode
& S_IFMT
) == S_IFDIR
) {
666 pstrcpy (path
, ino_list
[i
].dos_path
);
668 /* promote it for future use */
669 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
673 /* If the inode is different then something's changed,
674 scrub the entry and start from scratch. */
675 ino_list
[i
].valid
= False
;
682 /* We don't have the information to hand so rely on traditional methods.
683 The very slow getcwd, which spawns a process on some systems, or the
684 not quite so bad getwd. */
686 if (!vfs_getwd(conn
,s
)) {
687 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno
)));
693 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s
,(double)st
.st_ino
,(double)st
.st_dev
));
695 /* add it to the cache */
696 i
= MAX_GETWDCACHE
- 1;
697 string_set(&ino_list
[i
].dos_path
,s
);
698 ino_list
[i
].dev
= st
.st_dev
;
699 ino_list
[i
].inode
= st
.st_ino
;
700 ino_list
[i
].valid
= True
;
702 /* put it at the top of the list */
703 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
709 /* check if the file 'nmae' is a symlink, in that case check that it point to
710 a file that reside under the 'dir' tree */
712 static BOOL
readlink_check(connection_struct
*conn
, char *dir
, char *name
)
721 if (!vfs_GetWd(conn
, savedir
)) {
722 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name
, dir
));
726 if (vfs_ChDir(conn
, dir
) != 0) {
727 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir
));
731 if (!vfs_GetWd(conn
, realdir
)) {
732 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir
));
733 vfs_ChDir(conn
, savedir
);
737 reallen
= strlen(realdir
);
738 if (realdir
[reallen
-1] == '/') {
740 realdir
[reallen
] = 0;
743 if (conn
->vfs_ops
.readlink(conn
, name
, flink
, sizeof(pstring
) -1) != -1) {
744 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name
));
746 pstrcpy(cleanlink
, flink
);
748 pstrcpy(cleanlink
, realdir
);
749 pstrcat(cleanlink
, "/");
750 pstrcat(cleanlink
, flink
);
752 unix_clean_name(cleanlink
);
754 if (strncmp(cleanlink
, realdir
, reallen
) != 0) {
755 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name
, realdir
, cleanlink
, (int)reallen
));
760 vfs_ChDir(conn
, savedir
);
765 /*******************************************************************
766 Reduce a file name, removing .. elements and checking that
767 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
768 on the system that has the referenced file system.
769 Widelinks are allowed if widelinks is true.
770 ********************************************************************/
772 BOOL
reduce_name(connection_struct
*conn
, char *s
,char *dir
,BOOL widelinks
)
782 BOOL relative
= (*s
!= '/');
784 *dir2
= *wd
= *base_name
= *newname
= 0;
787 /* can't have a leading .. */
788 if (strncmp(s
,"..",2) == 0 && (s
[2]==0 || s
[2]=='/')) {
789 DEBUG(3,("Illegal file name? (%s)\n",s
));
799 DEBUG(3,("reduce_name [%s] [%s]\n",s
,dir
));
801 /* remove any double slashes */
802 all_string_sub(s
,"//","/",0);
804 pstrcpy(base_name
,s
);
805 p
= strrchr(base_name
,'/');
808 return readlink_check(conn
, dir
, s
);
810 if (!vfs_GetWd(conn
,wd
)) {
811 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s
,dir
));
815 if (vfs_ChDir(conn
,dir
) != 0) {
816 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir
));
820 if (!vfs_GetWd(conn
,dir2
)) {
821 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir
));
826 if (p
&& (p
!= base_name
)) {
828 if (strcmp(p
+1,".")==0)
830 if (strcmp(p
+1,"..")==0)
834 if (vfs_ChDir(conn
,base_name
) != 0) {
836 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s
,dir
,base_name
));
840 if (!vfs_GetWd(conn
,newname
)) {
842 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s
,base_name
));
846 if (p
&& (p
!= base_name
)) {
847 pstrcat(newname
,"/");
848 pstrcat(newname
,p
+1);
852 size_t l
= strlen(dir2
);
853 if (dir2
[l
-1] == '/')
856 if (strncmp(newname
,dir2
,l
) != 0) {
858 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s
,dir2
,newname
,(int)l
));
862 if (!readlink_check(conn
, dir
, newname
)) {
863 DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s
));
868 if (newname
[l
] == '/')
869 pstrcpy(s
,newname
+ l
+ 1);
871 pstrcpy(s
,newname
+l
);
881 DEBUG(3,("reduced to %s\n",s
));