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_OFF_T len
)
353 connection_struct
*conn
= fsp
->conn
;
354 struct vfs_ops
*vfs_ops
= &conn
->vfs_ops
;
355 SMB_OFF_T 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 ret
= vfs_fstat(fsp
,fsp
->fd
,&st
);
370 if (len
== st
.st_size
)
373 if (len
< st
.st_size
) {
374 /* Shrink - use ftruncate. */
376 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
377 fsp
->fsp_name
, (double)st
.st_size
));
379 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
380 if ((ret
= vfs_ops
->ftruncate(fsp
, fsp
->fd
, len
)) != -1) {
381 set_filelen_write_cache(fsp
, len
);
386 /* Grow - we need to test if we have enough space. */
388 if (!lp_strict_allocate(SNUM(fsp
->conn
)))
392 len
/= 1024; /* Len is now number of 1k blocks needed. */
393 space_avail
= (SMB_OFF_T
)conn
->vfs_ops
.disk_free(conn
,fsp
->fsp_name
,False
,&bsize
,&dfree
,&dsize
);
395 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
396 fsp
->fsp_name
, (double)st
.st_size
, (unsigned long)len
, (unsigned long)space_avail
));
398 if (len
> space_avail
) {
406 /****************************************************************************
407 A vfs set_filelen call.
408 set the length of a file from a filedescriptor.
409 Returns 0 on success, -1 on failure.
410 ****************************************************************************/
412 int vfs_set_filelen(files_struct
*fsp
, SMB_OFF_T len
)
416 release_level_2_oplocks_on_change(fsp
);
417 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp
->fsp_name
, (double)len
));
418 flush_write_cache(fsp
, SIZECHANGE_FLUSH
);
419 if ((ret
= fsp
->conn
->vfs_ops
.ftruncate(fsp
, fsp
->fd
, len
)) != -1)
420 set_filelen_write_cache(fsp
, len
);
425 /****************************************************************************
426 Transfer some data (n bytes) between two file_struct's.
427 ****************************************************************************/
429 static files_struct
*in_fsp
;
430 static files_struct
*out_fsp
;
432 static ssize_t
read_fn(int fd
, void *buf
, size_t len
)
434 return in_fsp
->conn
->vfs_ops
.read(in_fsp
, fd
, buf
, len
);
437 static ssize_t
write_fn(int fd
, const void *buf
, size_t len
)
439 return out_fsp
->conn
->vfs_ops
.write(out_fsp
, fd
, buf
, len
);
442 SMB_OFF_T
vfs_transfer_file(files_struct
*in
, files_struct
*out
, SMB_OFF_T n
)
447 return transfer_file_internal(in_fsp
->fd
, out_fsp
->fd
, n
, read_fn
, write_fn
);
450 /*******************************************************************
451 A vfs_readdir wrapper which just returns the file name.
452 ********************************************************************/
454 char *vfs_readdirname(connection_struct
*conn
, void *p
)
462 ptr
= (struct dirent
*)conn
->vfs_ops
.readdir(conn
,p
);
473 #ifdef HAVE_BROKEN_READDIR
474 /* using /usr/ucb/cc is BAD */
480 memcpy(buf
, dname
, NAMLEN(ptr
)+1);
488 /* VFS options not quite working yet */
492 /***************************************************************************
493 handle the interpretation of the vfs option parameter
494 *************************************************************************/
495 static BOOL
handle_vfs_option(char *pszParmValue
, char **ptr
)
497 struct vfs_options
*new_option
, **options
= (struct vfs_options
**)ptr
;
500 /* Create new vfs option */
502 new_option
= (struct vfs_options
*)malloc(sizeof(*new_option
));
503 if (new_option
== NULL
) {
507 ZERO_STRUCTP(new_option
);
509 /* Get name and value */
511 new_option
->name
= strtok(pszParmValue
, "=");
513 if (new_option
->name
== NULL
) {
517 while(isspace(*new_option
->name
)) {
521 for (i
= strlen(new_option
->name
); i
> 0; i
--) {
522 if (!isspace(new_option
->name
[i
- 1])) break;
525 new_option
->name
[i
] = '\0';
526 new_option
->name
= strdup(new_option
->name
);
528 new_option
->value
= strtok(NULL
, "=");
530 if (new_option
->value
!= NULL
) {
532 while(isspace(*new_option
->value
)) {
536 for (i
= strlen(new_option
->value
); i
> 0; i
--) {
537 if (!isspace(new_option
->value
[i
- 1])) break;
540 new_option
->value
[i
] = '\0';
541 new_option
->value
= strdup(new_option
->value
);
546 DLIST_ADD(*options
, new_option
);
554 /*******************************************************************
555 A wrapper for vfs_chdir().
556 ********************************************************************/
558 int vfs_ChDir(connection_struct
*conn
, const char *path
)
561 static pstring LastDir
="";
563 if (strcsequal(path
,"."))
566 if (*path
== '/' && strcsequal(LastDir
,path
))
569 DEBUG(3,("vfs_ChDir to %s\n",path
));
571 res
= vfs_chdir(conn
,path
);
573 pstrcpy(LastDir
,path
);
577 /* number of list structures for a caching GetWd function. */
578 #define MAX_GETWDCACHE (50)
581 SMB_DEV_T dev
; /* These *must* be compatible with the types returned in a stat() call. */
582 SMB_INO_T inode
; /* These *must* be compatible with the types returned in a stat() call. */
583 char *dos_path
; /* The pathname in DOS format. */
585 } ino_list
[MAX_GETWDCACHE
];
587 extern BOOL use_getwd_cache
;
589 /****************************************************************************
590 Prompte a ptr (to make it recently used)
591 ****************************************************************************/
593 static void array_promote(char *array
,int elsize
,int element
)
599 p
= (char *)malloc(elsize
);
602 DEBUG(5,("array_promote: malloc fail\n"));
606 memcpy(p
,array
+ element
* elsize
, elsize
);
607 memmove(array
+ elsize
,array
,elsize
*element
);
608 memcpy(array
,p
,elsize
);
612 /*******************************************************************
613 Return the absolute current directory path - given a UNIX pathname.
614 Note that this path is returned in DOS format, not UNIX
615 format. Note this can be called with conn == NULL.
616 ********************************************************************/
618 char *vfs_GetWd(connection_struct
*conn
, char *path
)
621 static BOOL getwd_cache_init
= False
;
622 SMB_STRUCT_STAT st
, st2
;
627 if (!use_getwd_cache
)
628 return(vfs_getwd(conn
,path
));
631 if (!getwd_cache_init
) {
632 getwd_cache_init
= True
;
633 for (i
=0;i
<MAX_GETWDCACHE
;i
++) {
634 string_set(&ino_list
[i
].dos_path
,"");
635 ino_list
[i
].valid
= False
;
639 /* Get the inode of the current directory, if this doesn't work we're
642 if (vfs_stat(conn
, ".",&st
) == -1) {
643 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path
));
644 return(vfs_getwd(conn
,path
));
648 for (i
=0; i
<MAX_GETWDCACHE
; i
++) {
649 if (ino_list
[i
].valid
) {
651 /* If we have found an entry with a matching inode and dev number
652 then find the inode number for the directory in the cached string.
653 If this agrees with that returned by the stat for the current
654 directory then all is o.k. (but make sure it is a directory all
657 if (st
.st_ino
== ino_list
[i
].inode
&& st
.st_dev
== ino_list
[i
].dev
) {
658 if (vfs_stat(conn
,ino_list
[i
].dos_path
,&st2
) == 0) {
659 if (st
.st_ino
== st2
.st_ino
&& st
.st_dev
== st2
.st_dev
&&
660 (st2
.st_mode
& S_IFMT
) == S_IFDIR
) {
661 pstrcpy (path
, ino_list
[i
].dos_path
);
663 /* promote it for future use */
664 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
668 /* If the inode is different then something's changed,
669 scrub the entry and start from scratch. */
670 ino_list
[i
].valid
= False
;
677 /* We don't have the information to hand so rely on traditional methods.
678 The very slow getcwd, which spawns a process on some systems, or the
679 not quite so bad getwd. */
681 if (!vfs_getwd(conn
,s
)) {
682 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno
)));
688 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s
,(double)st
.st_ino
,(double)st
.st_dev
));
690 /* add it to the cache */
691 i
= MAX_GETWDCACHE
- 1;
692 string_set(&ino_list
[i
].dos_path
,s
);
693 ino_list
[i
].dev
= st
.st_dev
;
694 ino_list
[i
].inode
= st
.st_ino
;
695 ino_list
[i
].valid
= True
;
697 /* put it at the top of the list */
698 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
703 /*******************************************************************
704 Reduce a file name, removing .. elements and checking that
705 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
706 on the system that has the referenced file system.
707 Widelinks are allowed if widelinks is true.
708 ********************************************************************/
710 BOOL
reduce_name(connection_struct
*conn
, char *s
,char *dir
,BOOL widelinks
)
720 BOOL relative
= (*s
!= '/');
722 *dir2
= *wd
= *base_name
= *newname
= 0;
726 /* can't have a leading .. */
727 if (strncmp(s
,"..",2) == 0 && (s
[2]==0 || s
[2]=='/')) {
728 DEBUG(3,("Illegal file name? (%s)\n",s
));
738 DEBUG(3,("reduce_name [%s] [%s]\n",s
,dir
));
740 /* remove any double slashes */
741 all_string_sub(s
,"//","/",0);
743 pstrcpy(base_name
,s
);
744 p
= strrchr(base_name
,'/');
749 if (!vfs_GetWd(conn
,wd
)) {
750 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s
,dir
));
754 if (vfs_ChDir(conn
,dir
) != 0) {
755 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir
));
759 if (!vfs_GetWd(conn
,dir2
)) {
760 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir
));
765 if (p
&& (p
!= base_name
)) {
767 if (strcmp(p
+1,".")==0)
769 if (strcmp(p
+1,"..")==0)
773 if (vfs_ChDir(conn
,base_name
) != 0) {
775 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s
,dir
,base_name
));
779 if (!vfs_GetWd(conn
,newname
)) {
781 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s
,dir2
));
785 if (p
&& (p
!= base_name
)) {
786 pstrcat(newname
,"/");
787 pstrcat(newname
,p
+1);
791 size_t l
= strlen(dir2
);
792 if (dir2
[l
-1] == '/')
795 if (strncmp(newname
,dir2
,l
) != 0) {
797 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s
,dir2
,newname
,(int)l
));
802 if (newname
[l
] == '/')
803 pstrcpy(s
,newname
+ l
+ 1);
805 pstrcpy(s
,newname
+l
);
815 DEBUG(3,("reduced to %s\n",s
));