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 static struct vfs_ops default_vfs_ops = {
39 vfswrap_dummy_connect,
40 vfswrap_dummy_disconnect,
43 /* Directory operations */
83 #if defined(HAVE_NO_ACLS)
92 /****************************************************************************
93 initialise default vfs hooks
94 ****************************************************************************/
96 static BOOL vfs_init_default(connection_struct *conn)
98 DEBUG(3, ("Initialising default vfs hooks\n"));
100 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
104 /****************************************************************************
105 initialise custom vfs hooks
106 ****************************************************************************/
109 static BOOL vfs_init_custom(connection_struct *conn)
111 int vfs_version = -1;
112 struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
114 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
115 lp_vfsobj(SNUM(conn))));
117 /* Open object file */
118 if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)),
119 RTLD_NOW | RTLD_GLOBAL)) == NULL) {
120 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), sys_dlerror()));
124 /* Get handle on vfs_init() symbol */
125 init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
127 if (init_fptr == NULL) {
128 DEBUG(0, ("No vfs_init() symbol found in %s\n",
129 lp_vfsobj(SNUM(conn))));
133 /* Initialise vfs_ops structure */
134 conn->vfs_ops = default_vfs_ops;
136 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops)) == NULL) {
137 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
141 if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
142 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
143 vfs_version, SMB_VFS_INTERFACE_VERSION ));
147 if (ops != &conn->vfs_ops) {
148 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
155 /*****************************************************************
157 ******************************************************************/
159 BOOL smbd_vfs_init(connection_struct *conn)
161 if (*lp_vfsobj(SNUM(conn))) {
164 /* Loadable object file */
166 if (!vfs_init_custom(conn)) {
167 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
173 DEBUG(0, ("smbd_vfs_init: No libdl present - cannot use VFS objects\n"));
178 /* Normal share - initialise with disk access functions */
180 return vfs_init_default(conn);
183 /*******************************************************************
184 Check if directory exists.
185 ********************************************************************/
187 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
195 if (vfs_stat(conn,dname,st) != 0)
198 ret = S_ISDIR(st->st_mode);
205 /*******************************************************************
207 ********************************************************************/
208 char *vfs_getwd(connection_struct *conn, char *path)
210 return conn->vfs_ops.getwd(conn,path);
213 /*******************************************************************
215 ********************************************************************/
217 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
220 SMB_STRUCT_STAT sbuf;
222 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
224 * Check if high bits should have been set,
225 * then (if bits are missing): add them.
226 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
228 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
229 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
230 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
235 /*******************************************************************
236 Check if an object exists in the vfs.
237 ********************************************************************/
239 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
248 if (vfs_stat(conn,fname,sbuf) == -1)
253 /*******************************************************************
254 Check if a file exists in the vfs.
255 ********************************************************************/
257 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
266 if (vfs_stat(conn,fname,sbuf) == -1)
268 return(S_ISREG(sbuf->st_mode));
271 /****************************************************************************
272 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
273 ****************************************************************************/
275 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
279 while (total < byte_count)
281 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
284 if (ret == 0) return total;
293 return (ssize_t)total;
296 /****************************************************************************
297 Write data to a fd on the vfs.
298 ****************************************************************************/
300 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
306 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
315 return (ssize_t)total;
318 /****************************************************************************
319 An allocate file space call using the vfs interface.
320 Allocates space for a file from a filedescriptor.
321 Returns 0 on success, -1 on failure.
322 ****************************************************************************/
324 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
328 connection_struct *conn = fsp->conn;
329 struct vfs_ops *vfs_ops = &conn->vfs_ops;
330 SMB_OFF_T space_avail;
331 SMB_BIG_UINT bsize,dfree,dsize;
333 release_level_2_oplocks_on_change(fsp);
336 * Actually try and commit the space on disk....
339 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
341 ret = vfs_fstat(fsp,fsp->fd,&st);
345 if (len == st.st_size)
348 if (len < st.st_size) {
349 /* Shrink - use ftruncate. */
351 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
352 fsp->fsp_name, (double)st.st_size ));
354 flush_write_cache(fsp, SIZECHANGE_FLUSH);
355 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
356 set_filelen_write_cache(fsp, len);
361 /* Grow - we need to test if we have enough space. */
363 if (!lp_strict_allocate(SNUM(fsp->conn)))
367 len /= 1024; /* Len is now number of 1k blocks needed. */
368 space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
370 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
371 fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
373 if (len > space_avail) {
381 /****************************************************************************
382 A vfs set_filelen call.
383 set the length of a file from a filedescriptor.
384 Returns 0 on success, -1 on failure.
385 ****************************************************************************/
387 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
391 release_level_2_oplocks_on_change(fsp);
392 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
393 flush_write_cache(fsp, SIZECHANGE_FLUSH);
394 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
395 set_filelen_write_cache(fsp, len);
400 /****************************************************************************
401 Transfer some data (n bytes) between two file_struct's.
402 ****************************************************************************/
404 static files_struct *in_fsp;
405 static files_struct *out_fsp;
407 static ssize_t read_fn(int fd, void *buf, size_t len)
409 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
412 static ssize_t write_fn(int fd, const void *buf, size_t len)
414 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
417 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
422 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
425 /*******************************************************************
426 A vfs_readdir wrapper which just returns the file name.
427 ********************************************************************/
429 char *vfs_readdirname(connection_struct *conn, void *p)
437 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
448 #ifdef HAVE_BROKEN_READDIR
449 /* using /usr/ucb/cc is BAD */
456 /* VFS options not quite working yet */
460 /***************************************************************************
461 handle the interpretation of the vfs option parameter
462 *************************************************************************/
463 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
465 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
468 /* Create new vfs option */
470 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
471 if (new_option == NULL) {
475 ZERO_STRUCTP(new_option);
477 /* Get name and value */
479 new_option->name = strtok(pszParmValue, "=");
481 if (new_option->name == NULL) {
485 while(isspace(*new_option->name)) {
489 for (i = strlen(new_option->name); i > 0; i--) {
490 if (!isspace(new_option->name[i - 1])) break;
493 new_option->name[i] = '\0';
494 new_option->name = strdup(new_option->name);
496 new_option->value = strtok(NULL, "=");
498 if (new_option->value != NULL) {
500 while(isspace(*new_option->value)) {
504 for (i = strlen(new_option->value); i > 0; i--) {
505 if (!isspace(new_option->value[i - 1])) break;
508 new_option->value[i] = '\0';
509 new_option->value = strdup(new_option->value);
514 DLIST_ADD(*options, new_option);
522 /*******************************************************************
523 A wrapper for vfs_chdir().
524 ********************************************************************/
526 int vfs_ChDir(connection_struct *conn, char *path)
529 static pstring LastDir="";
531 if (strcsequal(path,"."))
534 if (*path == '/' && strcsequal(LastDir,path))
537 DEBUG(3,("vfs_ChDir to %s\n",path));
539 res = vfs_chdir(conn,path);
541 pstrcpy(LastDir,path);
545 /* number of list structures for a caching GetWd function. */
546 #define MAX_GETWDCACHE (50)
550 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
551 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
552 char *dos_path; /* The pathname in DOS format. */
554 } ino_list[MAX_GETWDCACHE];
556 extern BOOL use_getwd_cache;
558 /****************************************************************************
559 Prompte a ptr (to make it recently used)
560 ****************************************************************************/
562 static void array_promote(char *array,int elsize,int element)
568 p = (char *)malloc(elsize);
571 DEBUG(5,("array_promote: malloc fail\n"));
575 memcpy(p,array + element * elsize, elsize);
576 memmove(array + elsize,array,elsize*element);
577 memcpy(array,p,elsize);
581 /*******************************************************************
582 Return the absolute current directory path - given a UNIX pathname.
583 Note that this path is returned in DOS format, not UNIX
584 format. Note this can be called with conn == NULL.
585 ********************************************************************/
587 char *vfs_GetWd(connection_struct *conn, char *path)
590 static BOOL getwd_cache_init = False;
591 SMB_STRUCT_STAT st, st2;
596 if (!use_getwd_cache)
597 return(vfs_getwd(conn,path));
600 if (!getwd_cache_init)
602 getwd_cache_init = True;
603 for (i=0;i<MAX_GETWDCACHE;i++)
605 string_set(&ino_list[i].dos_path,"");
606 ino_list[i].valid = False;
610 /* Get the inode of the current directory, if this doesn't work we're
613 if (vfs_stat(conn, ".",&st) == -1)
615 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
616 return(vfs_getwd(conn,path));
620 for (i=0; i<MAX_GETWDCACHE; i++)
621 if (ino_list[i].valid)
624 /* If we have found an entry with a matching inode and dev number
625 then find the inode number for the directory in the cached string.
626 If this agrees with that returned by the stat for the current
627 directory then all is o.k. (but make sure it is a directory all
630 if (st.st_ino == ino_list[i].inode &&
631 st.st_dev == ino_list[i].dev)
633 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
635 if (st.st_ino == st2.st_ino &&
636 st.st_dev == st2.st_dev &&
637 (st2.st_mode & S_IFMT) == S_IFDIR)
639 pstrcpy (path, ino_list[i].dos_path);
641 /* promote it for future use */
642 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
647 /* If the inode is different then something's changed,
648 scrub the entry and start from scratch. */
649 ino_list[i].valid = False;
656 /* We don't have the information to hand so rely on traditional methods.
657 The very slow getcwd, which spawns a process on some systems, or the
658 not quite so bad getwd. */
660 if (!vfs_getwd(conn,s))
662 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
668 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
670 /* add it to the cache */
671 i = MAX_GETWDCACHE - 1;
672 string_set(&ino_list[i].dos_path,s);
673 ino_list[i].dev = st.st_dev;
674 ino_list[i].inode = st.st_ino;
675 ino_list[i].valid = True;
677 /* put it at the top of the list */
678 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
683 /*******************************************************************
684 Reduce a file name, removing .. elements and checking that
685 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
686 on the system that has the referenced file system.
687 Widelinks are allowed if widelinks is true.
688 ********************************************************************/
690 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
700 BOOL relative = (*s != '/');
702 *dir2 = *wd = *base_name = *newname = 0;
707 /* can't have a leading .. */
708 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
710 DEBUG(3,("Illegal file name? (%s)\n",s));
720 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
722 /* remove any double slashes */
723 all_string_sub(s,"//","/",0);
725 pstrcpy(base_name,s);
726 p = strrchr_m(base_name,'/');
731 if (!vfs_GetWd(conn,wd))
733 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
737 if (vfs_ChDir(conn,dir) != 0)
739 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
743 if (!vfs_GetWd(conn,dir2))
745 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
750 if (p && (p != base_name))
753 if (strcmp(p+1,".")==0)
755 if (strcmp(p+1,"..")==0)
759 if (vfs_ChDir(conn,base_name) != 0)
762 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
766 if (!vfs_GetWd(conn,newname))
769 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
773 if (p && (p != base_name))
775 pstrcat(newname,"/");
776 pstrcat(newname,p+1);
780 size_t l = strlen(dir2);
781 if (dir2[l-1] == '/')
784 if (strncmp(newname,dir2,l) != 0)
787 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
793 if (newname[l] == '/')
794 pstrcpy(s,newname + l + 1);
796 pstrcpy(s,newname+l);
807 DEBUG(3,("reduced to %s\n",s));