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 extern int DEBUGLEVEL
;
26 /* Some structures to help us initialise the vfs operations table */
33 /* Default vfs hooks. WARNING: The order of these initialisers is
34 very important. They must be in the same order as defined in
35 vfs.h. Change at your own peril. */
37 struct vfs_ops default_vfs_ops
= {
41 vfswrap_dummy_connect
,
42 vfswrap_dummy_disconnect
,
45 /* Directory operations */
78 #if defined(HAVE_NO_ACLS)
87 /****************************************************************************
88 initialise default vfs hooks
89 ****************************************************************************/
90 int vfs_init_default(connection_struct
*conn
)
92 DEBUG(3, ("Initialising default vfs hooks\n"));
94 memcpy(&conn
->vfs_ops
, &default_vfs_ops
, sizeof(struct vfs_ops
));
98 /****************************************************************************
99 initialise custom vfs hooks
100 ****************************************************************************/
103 BOOL
vfs_init_custom(connection_struct
*conn
)
105 int vfs_version
= -1;
106 struct vfs_ops
*ops
, *(*init_fptr
)(int *);
108 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
109 lp_vfsobj(SNUM(conn
))));
111 /* Open object file */
113 if ((conn
->dl_handle
= sys_dlopen(lp_vfsobj(SNUM(conn
)), RTLD_NOW
| RTLD_GLOBAL
)) == NULL
) {
114 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn
)), dlerror()));
118 /* Get handle on vfs_init() symbol */
120 init_fptr
= (struct vfs_ops
*(*)(int *))sys_dlsym(conn
->dl_handle
, "vfs_init");
122 if (init_fptr
== NULL
) {
123 DEBUG(0, ("No vfs_init() symbol found in %s\n",
124 lp_vfsobj(SNUM(conn
))));
128 /* Initialise vfs_ops structure */
130 if ((ops
= init_fptr(&vfs_version
)) == NULL
) {
131 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn
))));
135 if (vfs_version
!= SMB_VFS_INTERFACE_VERSION
) {
136 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
137 vfs_version
, SMB_VFS_INTERFACE_VERSION
));
141 /* Fill in unused operations with default (disk based) ones.
142 There's probably a neater way to do this then a whole bunch of
145 memcpy(&conn
->vfs_ops
, ops
, sizeof(struct vfs_ops
));
147 if (conn
->vfs_ops
.connect
== NULL
) {
148 conn
->vfs_ops
.connect
= default_vfs_ops
.connect
;
151 if (conn
->vfs_ops
.disconnect
== NULL
) {
152 conn
->vfs_ops
.disconnect
= default_vfs_ops
.disconnect
;
155 if (conn
->vfs_ops
.disk_free
== NULL
) {
156 conn
->vfs_ops
.disk_free
= default_vfs_ops
.disk_free
;
159 if (conn
->vfs_ops
.opendir
== NULL
) {
160 conn
->vfs_ops
.opendir
= default_vfs_ops
.opendir
;
163 if (conn
->vfs_ops
.readdir
== NULL
) {
164 conn
->vfs_ops
.readdir
= default_vfs_ops
.readdir
;
167 if (conn
->vfs_ops
.mkdir
== NULL
) {
168 conn
->vfs_ops
.mkdir
= default_vfs_ops
.mkdir
;
171 if (conn
->vfs_ops
.rmdir
== NULL
) {
172 conn
->vfs_ops
.rmdir
= default_vfs_ops
.rmdir
;
175 if (conn
->vfs_ops
.closedir
== NULL
) {
176 conn
->vfs_ops
.closedir
= default_vfs_ops
.closedir
;
179 if (conn
->vfs_ops
.open
== NULL
) {
180 conn
->vfs_ops
.open
= default_vfs_ops
.open
;
183 if (conn
->vfs_ops
.close
== NULL
) {
184 conn
->vfs_ops
.close
= default_vfs_ops
.close
;
187 if (conn
->vfs_ops
.read
== NULL
) {
188 conn
->vfs_ops
.read
= default_vfs_ops
.read
;
191 if (conn
->vfs_ops
.write
== NULL
) {
192 conn
->vfs_ops
.write
= default_vfs_ops
.write
;
195 if (conn
->vfs_ops
.lseek
== NULL
) {
196 conn
->vfs_ops
.lseek
= default_vfs_ops
.lseek
;
199 if (conn
->vfs_ops
.rename
== NULL
) {
200 conn
->vfs_ops
.rename
= default_vfs_ops
.rename
;
203 if (conn
->vfs_ops
.fsync
== NULL
) {
204 conn
->vfs_ops
.fsync
= default_vfs_ops
.fsync
;
207 if (conn
->vfs_ops
.stat
== NULL
) {
208 conn
->vfs_ops
.stat
= default_vfs_ops
.stat
;
211 if (conn
->vfs_ops
.fstat
== NULL
) {
212 conn
->vfs_ops
.fstat
= default_vfs_ops
.fstat
;
215 if (conn
->vfs_ops
.lstat
== NULL
) {
216 conn
->vfs_ops
.lstat
= default_vfs_ops
.lstat
;
219 if (conn
->vfs_ops
.unlink
== NULL
) {
220 conn
->vfs_ops
.unlink
= default_vfs_ops
.unlink
;
223 if (conn
->vfs_ops
.chmod
== NULL
) {
224 conn
->vfs_ops
.chmod
= default_vfs_ops
.chmod
;
227 if (conn
->vfs_ops
.chown
== NULL
) {
228 conn
->vfs_ops
.chown
= default_vfs_ops
.chown
;
231 if (conn
->vfs_ops
.chdir
== NULL
) {
232 conn
->vfs_ops
.chdir
= default_vfs_ops
.chdir
;
235 if (conn
->vfs_ops
.getwd
== NULL
) {
236 conn
->vfs_ops
.getwd
= default_vfs_ops
.getwd
;
239 if (conn
->vfs_ops
.utime
== NULL
) {
240 conn
->vfs_ops
.utime
= default_vfs_ops
.utime
;
243 if (conn
->vfs_ops
.ftruncate
== NULL
) {
244 conn
->vfs_ops
.ftruncate
= default_vfs_ops
.ftruncate
;
247 if (conn
->vfs_ops
.lock
== NULL
) {
248 conn
->vfs_ops
.lock
= default_vfs_ops
.lock
;
251 if (conn
->vfs_ops
.fget_nt_acl
== NULL
) {
252 conn
->vfs_ops
.fget_nt_acl
= default_vfs_ops
.fget_nt_acl
;
255 if (conn
->vfs_ops
.get_nt_acl
== NULL
) {
256 conn
->vfs_ops
.get_nt_acl
= default_vfs_ops
.get_nt_acl
;
259 if (conn
->vfs_ops
.fset_nt_acl
== NULL
) {
260 conn
->vfs_ops
.fset_nt_acl
= default_vfs_ops
.fset_nt_acl
;
263 if (conn
->vfs_ops
.set_nt_acl
== NULL
) {
264 conn
->vfs_ops
.set_nt_acl
= default_vfs_ops
.set_nt_acl
;
267 if (conn
->vfs_ops
.chmod_acl
== NULL
) {
268 conn
->vfs_ops
.chmod_acl
= default_vfs_ops
.chmod_acl
;
271 if (conn
->vfs_ops
.fchmod_acl
== NULL
) {
272 conn
->vfs_ops
.fchmod_acl
= default_vfs_ops
.fchmod_acl
;
278 /*******************************************************************
279 Check if directory exists.
280 ********************************************************************/
282 BOOL
vfs_directory_exist(connection_struct
*conn
, char *dname
, SMB_STRUCT_STAT
*st
)
290 if (vfs_stat(conn
,dname
,st
) != 0)
293 ret
= S_ISDIR(st
->st_mode
);
300 /*******************************************************************
301 vfs mkdir wrapper that calls dos_to_unix.
302 ********************************************************************/
304 int vfs_mkdir(connection_struct
*conn
, char *fname
, mode_t mode
)
308 SMB_STRUCT_STAT sbuf
;
310 pstrcpy(name
,dos_to_unix(fname
,False
)); /* paranoia copy */
311 if(!(ret
=conn
->vfs_ops
.mkdir(conn
,name
,mode
))) {
313 * Check if high bits should have been set,
314 * then (if bits are missing): add them.
315 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
317 if(mode
& ~(S_IRWXU
|S_IRWXG
|S_IRWXO
) &&
318 !vfs_stat(conn
,name
,&sbuf
) && (mode
& ~sbuf
.st_mode
))
319 vfs_chmod(conn
,name
,sbuf
.st_mode
| (mode
& ~sbuf
.st_mode
));
324 /*******************************************************************
325 vfs getwd wrapper that calls dos_to_unix.
326 ********************************************************************/
328 char *vfs_getwd(connection_struct
*conn
, char *unix_path
)
331 wd
= conn
->vfs_ops
.getwd(conn
,unix_path
);
333 unix_to_dos(wd
, True
);
337 /*******************************************************************
338 Check if a vfs file exists.
339 ********************************************************************/
341 BOOL
vfs_file_exist(connection_struct
*conn
,char *fname
,SMB_STRUCT_STAT
*sbuf
)
350 if (vfs_stat(conn
,fname
,sbuf
) != 0)
353 return(S_ISREG(sbuf
->st_mode
));
356 /****************************************************************************
357 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
358 ****************************************************************************/
360 ssize_t
vfs_read_data(files_struct
*fsp
, char *buf
, size_t byte_count
)
364 while (total
< byte_count
)
366 ssize_t ret
= fsp
->conn
->vfs_ops
.read(fsp
, fsp
->fd
, buf
+ total
,
369 if (ret
== 0) return total
;
378 return (ssize_t
)total
;
381 /****************************************************************************
382 Write data to a fd on the vfs.
383 ****************************************************************************/
385 ssize_t
vfs_write_data(files_struct
*fsp
,char *buffer
,size_t N
)
392 ret
= fsp
->conn
->vfs_ops
.write(fsp
,fsp
->fd
,buffer
+ total
,N
- total
);
394 if (ret
== -1) return -1;
395 if (ret
== 0) return total
;
399 return (ssize_t
)total
;
402 /****************************************************************************
403 A vfs set_filelen call.
404 set the length of a file from a filedescriptor.
405 Returns 0 on success, -1 on failure.
406 ****************************************************************************/
408 int vfs_set_filelen(files_struct
*fsp
, SMB_OFF_T len
)
412 release_level_2_oplocks_on_change(fsp
);
413 if ((ret
= fsp
->conn
->vfs_ops
.ftruncate(fsp
, fsp
->fd
, len
)) != -1) {
414 set_filelen_write_cache(fsp
, len
);
420 /****************************************************************************
421 Transfer some data between two file_struct's.
422 ****************************************************************************/
424 SMB_OFF_T
vfs_transfer_file(int in_fd
, files_struct
*in_fsp
,
425 int out_fd
, files_struct
*out_fsp
,
426 SMB_OFF_T n
, char *header
, int headlen
, int align
)
428 static char *buf
=NULL
;
433 DEBUG(4,("vfs_transfer_file n=%.0f (head=%d) called\n",(double)n
,headlen
));
435 /* Check we have at least somewhere to read from */
437 SMB_ASSERT((in_fd
!= -1) || (in_fsp
!= NULL
));
440 size
= lp_readsize();
441 size
= MAX(size
,1024);
444 while (!buf
&& size
>0) {
445 buf
= (char *)Realloc(buf
,size
+8);
450 DEBUG(0,("Can't allocate transfer buffer!\n"));
454 abuf
= buf
+ (align
%8);
461 int s
= (int)MIN(n
,(SMB_OFF_T
)size
);
466 if (header
&& (headlen
>= MIN(s
,1024))) {
476 if (header
&& headlen
> 0)
478 ret
= MIN(headlen
,size
);
479 memcpy(buf1
,header
,ret
);
482 if (headlen
<= 0) header
= NULL
;
487 in_fsp
->conn
->vfs_ops
.read(in_fsp
,in_fsp
->fd
,buf1
+ret
,s
-ret
) : read(in_fd
,buf1
+ret
,s
-ret
);
492 ret2
= out_fsp
->conn
->vfs_ops
.write(out_fsp
,out_fsp
->fd
,buf1
,ret
);
494 ret2
= (out_fd
!= -1) ? write_data(out_fd
,buf1
,ret
) : ret
;
497 if (ret2
> 0) total
+= ret2
;
498 /* if we can't write then dump excess data */
500 vfs_transfer_file(in_fd
, in_fsp
, -1,NULL
,n
-(ret
+headlen
),NULL
,0,0);
502 if (ret
<= 0 || ret2
!= ret
)
509 /*******************************************************************
510 A vfs_readdir wrapper which just returns the file name.
511 ********************************************************************/
513 char *vfs_readdirname(connection_struct
*conn
, void *p
)
521 ptr
= (struct dirent
*)conn
->vfs_ops
.readdir(conn
,p
);
532 #ifdef HAVE_BROKEN_READDIR
533 /* using /usr/ucb/cc is BAD */
539 memcpy(buf
, dname
, NAMLEN(ptr
)+1);
540 unix_to_dos(buf
, True
);
547 /* VFS options not quite working yet */
551 /***************************************************************************
552 handle the interpretation of the vfs option parameter
553 *************************************************************************/
554 static BOOL
handle_vfs_option(char *pszParmValue
, char **ptr
)
556 struct vfs_options
*new_option
, **options
= (struct vfs_options
**)ptr
;
559 /* Create new vfs option */
561 new_option
= (struct vfs_options
*)malloc(sizeof(*new_option
));
562 if (new_option
== NULL
) {
566 ZERO_STRUCTP(new_option
);
568 /* Get name and value */
570 new_option
->name
= strtok(pszParmValue
, "=");
572 if (new_option
->name
== NULL
) {
576 while(isspace(*new_option
->name
)) {
580 for (i
= strlen(new_option
->name
); i
> 0; i
--) {
581 if (!isspace(new_option
->name
[i
- 1])) break;
584 new_option
->name
[i
] = '\0';
585 new_option
->name
= strdup(new_option
->name
);
587 new_option
->value
= strtok(NULL
, "=");
589 if (new_option
->value
!= NULL
) {
591 while(isspace(*new_option
->value
)) {
595 for (i
= strlen(new_option
->value
); i
> 0; i
--) {
596 if (!isspace(new_option
->value
[i
- 1])) break;
599 new_option
->value
[i
] = '\0';
600 new_option
->value
= strdup(new_option
->value
);
605 DLIST_ADD(*options
, new_option
);
613 /*******************************************************************
614 A wrapper for vfs_chdir().
615 ********************************************************************/
617 int vfs_ChDir(connection_struct
*conn
, char *path
)
620 static pstring LastDir
="";
622 if (strcsequal(path
,"."))
625 if (*path
== '/' && strcsequal(LastDir
,path
))
628 DEBUG(3,("vfs_ChDir to %s\n",path
));
630 res
= vfs_chdir(conn
,path
);
632 pstrcpy(LastDir
,path
);
636 /* number of list structures for a caching GetWd function. */
637 #define MAX_GETWDCACHE (50)
641 SMB_DEV_T dev
; /* These *must* be compatible with the types returned in a stat() call. */
642 SMB_INO_T inode
; /* These *must* be compatible with the types returned in a stat() call. */
643 char *dos_path
; /* The pathname in DOS format. */
645 } ino_list
[MAX_GETWDCACHE
];
647 extern BOOL use_getwd_cache
;
649 /****************************************************************************
650 Prompte a ptr (to make it recently used)
651 ****************************************************************************/
653 static void array_promote(char *array
,int elsize
,int element
)
659 p
= (char *)malloc(elsize
);
662 DEBUG(5,("array_promote: malloc fail\n"));
666 memcpy(p
,array
+ element
* elsize
, elsize
);
667 memmove(array
+ elsize
,array
,elsize
*element
);
668 memcpy(array
,p
,elsize
);
672 /*******************************************************************
673 Return the absolute current directory path - given a UNIX pathname.
674 Note that this path is returned in DOS format, not UNIX
675 format. Note this can be called with conn == NULL.
676 ********************************************************************/
678 char *vfs_GetWd(connection_struct
*conn
, char *path
)
681 static BOOL getwd_cache_init
= False
;
682 SMB_STRUCT_STAT st
, st2
;
687 if (!use_getwd_cache
)
688 return(vfs_getwd(conn
,path
));
691 if (!getwd_cache_init
)
693 getwd_cache_init
= True
;
694 for (i
=0;i
<MAX_GETWDCACHE
;i
++)
696 string_set(&ino_list
[i
].dos_path
,"");
697 ino_list
[i
].valid
= False
;
701 /* Get the inode of the current directory, if this doesn't work we're
704 if (vfs_stat(conn
, ".",&st
) == -1)
706 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path
));
707 return(vfs_getwd(conn
,path
));
711 for (i
=0; i
<MAX_GETWDCACHE
; i
++)
712 if (ino_list
[i
].valid
)
715 /* If we have found an entry with a matching inode and dev number
716 then find the inode number for the directory in the cached string.
717 If this agrees with that returned by the stat for the current
718 directory then all is o.k. (but make sure it is a directory all
721 if (st
.st_ino
== ino_list
[i
].inode
&&
722 st
.st_dev
== ino_list
[i
].dev
)
724 if (vfs_stat(conn
,ino_list
[i
].dos_path
,&st2
) == 0)
726 if (st
.st_ino
== st2
.st_ino
&&
727 st
.st_dev
== st2
.st_dev
&&
728 (st2
.st_mode
& S_IFMT
) == S_IFDIR
)
730 pstrcpy (path
, ino_list
[i
].dos_path
);
732 /* promote it for future use */
733 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
738 /* If the inode is different then something's changed,
739 scrub the entry and start from scratch. */
740 ino_list
[i
].valid
= False
;
747 /* We don't have the information to hand so rely on traditional methods.
748 The very slow getcwd, which spawns a process on some systems, or the
749 not quite so bad getwd. */
751 if (!vfs_getwd(conn
,s
))
753 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno
)));
759 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s
,(double)st
.st_ino
,(double)st
.st_dev
));
761 /* add it to the cache */
762 i
= MAX_GETWDCACHE
- 1;
763 string_set(&ino_list
[i
].dos_path
,s
);
764 ino_list
[i
].dev
= st
.st_dev
;
765 ino_list
[i
].inode
= st
.st_ino
;
766 ino_list
[i
].valid
= True
;
768 /* put it at the top of the list */
769 array_promote((char *)&ino_list
[0],sizeof(ino_list
[0]),i
);
774 /*******************************************************************
775 Reduce a file name, removing .. elements and checking that
776 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
777 on the system that has the referenced file system.
778 Widelinks are allowed if widelinks is true.
779 ********************************************************************/
781 BOOL
reduce_name(connection_struct
*conn
, char *s
,char *dir
,BOOL widelinks
)
791 BOOL relative
= (*s
!= '/');
793 *dir2
= *wd
= *base_name
= *newname
= 0;
798 /* can't have a leading .. */
799 if (strncmp(s
,"..",2) == 0 && (s
[2]==0 || s
[2]=='/'))
801 DEBUG(3,("Illegal file name? (%s)\n",s
));
811 DEBUG(3,("reduce_name [%s] [%s]\n",s
,dir
));
813 /* remove any double slashes */
814 all_string_sub(s
,"//","/",0);
816 pstrcpy(base_name
,s
);
817 p
= strrchr(base_name
,'/');
822 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)
830 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir
));
834 if (!vfs_GetWd(conn
,dir2
))
836 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir
));
841 if (p
&& (p
!= base_name
))
844 if (strcmp(p
+1,".")==0)
846 if (strcmp(p
+1,"..")==0)
850 if (vfs_ChDir(conn
,base_name
) != 0)
853 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s
,dir
,base_name
));
857 if (!vfs_GetWd(conn
,newname
))
860 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s
,dir2
));
864 if (p
&& (p
!= base_name
))
866 pstrcat(newname
,"/");
867 pstrcat(newname
,p
+1);
871 size_t l
= strlen(dir2
);
872 if (dir2
[l
-1] == '/')
875 if (strncmp(newname
,dir2
,l
) != 0)
878 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s
,dir2
,newname
,(int)l
));
884 if (newname
[l
] == '/')
885 pstrcpy(s
,newname
+ l
+ 1);
887 pstrcpy(s
,newname
+l
);
898 DEBUG(3,("reduced to %s\n",s
));