2 Unix SMB/CIFS implementation.
3 VFS initialisation and support functions
4 Copyright (C) Tim Potter 1999
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 /* Some structures to help us initialise the vfs operations table */
30 /* Default vfs hooks. WARNING: The order of these initialisers is
31 very important. They must be in the same order as defined in
32 vfs.h. Change at your own peril. */
34 static struct vfs_ops default_vfs_ops = {
38 vfswrap_dummy_connect,
39 vfswrap_dummy_disconnect,
42 /* Directory operations */
82 #if defined(HAVE_NO_ACLS)
91 /****************************************************************************
92 initialise default vfs hooks
93 ****************************************************************************/
95 static BOOL vfs_init_default(connection_struct *conn)
97 DEBUG(3, ("Initialising default vfs hooks\n"));
99 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
103 /****************************************************************************
104 initialise custom vfs hooks
105 ****************************************************************************/
108 static BOOL vfs_init_custom(connection_struct *conn)
110 int vfs_version = -1;
111 struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
113 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
114 lp_vfsobj(SNUM(conn))));
116 /* Open object file */
117 if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)),
118 RTLD_NOW | RTLD_GLOBAL)) == NULL) {
119 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), sys_dlerror()));
123 /* Get handle on vfs_init() symbol */
124 init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
126 if (init_fptr == NULL) {
127 DEBUG(0, ("No vfs_init() symbol found in %s\n",
128 lp_vfsobj(SNUM(conn))));
132 /* Initialise vfs_ops structure */
133 conn->vfs_ops = default_vfs_ops;
135 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops)) == NULL) {
136 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
140 if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
141 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
142 vfs_version, SMB_VFS_INTERFACE_VERSION ));
146 if (ops != &conn->vfs_ops) {
147 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
154 /*****************************************************************
156 ******************************************************************/
158 BOOL smbd_vfs_init(connection_struct *conn)
160 if (*lp_vfsobj(SNUM(conn))) {
163 /* Loadable object file */
165 if (!vfs_init_custom(conn)) {
166 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
172 DEBUG(0, ("smbd_vfs_init: No libdl present - cannot use VFS objects\n"));
177 /* Normal share - initialise with disk access functions */
179 return vfs_init_default(conn);
182 /*******************************************************************
183 Check if directory exists.
184 ********************************************************************/
186 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
194 if (vfs_stat(conn,dname,st) != 0)
197 ret = S_ISDIR(st->st_mode);
204 /*******************************************************************
206 ********************************************************************/
207 char *vfs_getwd(connection_struct *conn, char *path)
209 return conn->vfs_ops.getwd(conn,path);
212 /*******************************************************************
214 ********************************************************************/
216 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
219 SMB_STRUCT_STAT sbuf;
221 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
223 * Check if high bits should have been set,
224 * then (if bits are missing): add them.
225 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
227 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
228 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
229 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
234 /*******************************************************************
235 Check if an object exists in the vfs.
236 ********************************************************************/
238 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
247 if (vfs_stat(conn,fname,sbuf) == -1)
252 /*******************************************************************
253 Check if a file exists in the vfs.
254 ********************************************************************/
256 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
265 if (vfs_stat(conn,fname,sbuf) == -1)
267 return(S_ISREG(sbuf->st_mode));
270 /****************************************************************************
271 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
272 ****************************************************************************/
274 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
278 while (total < byte_count)
280 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
283 if (ret == 0) return total;
292 return (ssize_t)total;
295 /****************************************************************************
296 Write data to a fd on the vfs.
297 ****************************************************************************/
299 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
305 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
314 return (ssize_t)total;
317 /****************************************************************************
318 An allocate file space call using the vfs interface.
319 Allocates space for a file from a filedescriptor.
320 Returns 0 on success, -1 on failure.
321 ****************************************************************************/
323 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
327 connection_struct *conn = fsp->conn;
328 struct vfs_ops *vfs_ops = &conn->vfs_ops;
329 SMB_OFF_T space_avail;
330 SMB_BIG_UINT bsize,dfree,dsize;
332 release_level_2_oplocks_on_change(fsp);
335 * Actually try and commit the space on disk....
338 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
340 ret = vfs_fstat(fsp,fsp->fd,&st);
344 if (len == st.st_size)
347 if (len < st.st_size) {
348 /* Shrink - use ftruncate. */
350 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
351 fsp->fsp_name, (double)st.st_size ));
353 flush_write_cache(fsp, SIZECHANGE_FLUSH);
354 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
355 set_filelen_write_cache(fsp, len);
360 /* Grow - we need to test if we have enough space. */
362 if (!lp_strict_allocate(SNUM(fsp->conn)))
366 len /= 1024; /* Len is now number of 1k blocks needed. */
367 space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
369 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
370 fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
372 if (len > space_avail) {
380 /****************************************************************************
381 A vfs set_filelen call.
382 set the length of a file from a filedescriptor.
383 Returns 0 on success, -1 on failure.
384 ****************************************************************************/
386 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
390 release_level_2_oplocks_on_change(fsp);
391 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
392 flush_write_cache(fsp, SIZECHANGE_FLUSH);
393 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
394 set_filelen_write_cache(fsp, len);
399 /****************************************************************************
400 Transfer some data (n bytes) between two file_struct's.
401 ****************************************************************************/
403 static files_struct *in_fsp;
404 static files_struct *out_fsp;
406 static ssize_t read_fn(int fd, void *buf, size_t len)
408 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
411 static ssize_t write_fn(int fd, const void *buf, size_t len)
413 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
416 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
421 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
424 /*******************************************************************
425 A vfs_readdir wrapper which just returns the file name.
426 ********************************************************************/
428 char *vfs_readdirname(connection_struct *conn, void *p)
436 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
447 #ifdef HAVE_BROKEN_READDIR
448 /* using /usr/ucb/cc is BAD */
455 /* VFS options not quite working yet */
459 /***************************************************************************
460 handle the interpretation of the vfs option parameter
461 *************************************************************************/
462 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
464 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
467 /* Create new vfs option */
469 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
470 if (new_option == NULL) {
474 ZERO_STRUCTP(new_option);
476 /* Get name and value */
478 new_option->name = strtok(pszParmValue, "=");
480 if (new_option->name == NULL) {
484 while(isspace(*new_option->name)) {
488 for (i = strlen(new_option->name); i > 0; i--) {
489 if (!isspace(new_option->name[i - 1])) break;
492 new_option->name[i] = '\0';
493 new_option->name = strdup(new_option->name);
495 new_option->value = strtok(NULL, "=");
497 if (new_option->value != NULL) {
499 while(isspace(*new_option->value)) {
503 for (i = strlen(new_option->value); i > 0; i--) {
504 if (!isspace(new_option->value[i - 1])) break;
507 new_option->value[i] = '\0';
508 new_option->value = strdup(new_option->value);
513 DLIST_ADD(*options, new_option);
521 /*******************************************************************
522 A wrapper for vfs_chdir().
523 ********************************************************************/
525 int vfs_ChDir(connection_struct *conn, char *path)
528 static pstring LastDir="";
530 if (strcsequal(path,"."))
533 if (*path == '/' && strcsequal(LastDir,path))
536 DEBUG(3,("vfs_ChDir to %s\n",path));
538 res = vfs_chdir(conn,path);
540 pstrcpy(LastDir,path);
544 /* number of list structures for a caching GetWd function. */
545 #define MAX_GETWDCACHE (50)
549 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
550 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
551 char *dos_path; /* The pathname in DOS format. */
553 } ino_list[MAX_GETWDCACHE];
555 extern BOOL use_getwd_cache;
557 /****************************************************************************
558 Prompte a ptr (to make it recently used)
559 ****************************************************************************/
561 static void array_promote(char *array,int elsize,int element)
567 p = (char *)malloc(elsize);
570 DEBUG(5,("array_promote: malloc fail\n"));
574 memcpy(p,array + element * elsize, elsize);
575 memmove(array + elsize,array,elsize*element);
576 memcpy(array,p,elsize);
580 /*******************************************************************
581 Return the absolute current directory path - given a UNIX pathname.
582 Note that this path is returned in DOS format, not UNIX
583 format. Note this can be called with conn == NULL.
584 ********************************************************************/
586 char *vfs_GetWd(connection_struct *conn, char *path)
589 static BOOL getwd_cache_init = False;
590 SMB_STRUCT_STAT st, st2;
595 if (!use_getwd_cache)
596 return(vfs_getwd(conn,path));
599 if (!getwd_cache_init)
601 getwd_cache_init = True;
602 for (i=0;i<MAX_GETWDCACHE;i++)
604 string_set(&ino_list[i].dos_path,"");
605 ino_list[i].valid = False;
609 /* Get the inode of the current directory, if this doesn't work we're
612 if (vfs_stat(conn, ".",&st) == -1)
614 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
615 return(vfs_getwd(conn,path));
619 for (i=0; i<MAX_GETWDCACHE; i++)
620 if (ino_list[i].valid)
623 /* If we have found an entry with a matching inode and dev number
624 then find the inode number for the directory in the cached string.
625 If this agrees with that returned by the stat for the current
626 directory then all is o.k. (but make sure it is a directory all
629 if (st.st_ino == ino_list[i].inode &&
630 st.st_dev == ino_list[i].dev)
632 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
634 if (st.st_ino == st2.st_ino &&
635 st.st_dev == st2.st_dev &&
636 (st2.st_mode & S_IFMT) == S_IFDIR)
638 pstrcpy (path, ino_list[i].dos_path);
640 /* promote it for future use */
641 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
646 /* If the inode is different then something's changed,
647 scrub the entry and start from scratch. */
648 ino_list[i].valid = False;
655 /* We don't have the information to hand so rely on traditional methods.
656 The very slow getcwd, which spawns a process on some systems, or the
657 not quite so bad getwd. */
659 if (!vfs_getwd(conn,s))
661 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
667 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
669 /* add it to the cache */
670 i = MAX_GETWDCACHE - 1;
671 string_set(&ino_list[i].dos_path,s);
672 ino_list[i].dev = st.st_dev;
673 ino_list[i].inode = st.st_ino;
674 ino_list[i].valid = True;
676 /* put it at the top of the list */
677 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
682 /*******************************************************************
683 Reduce a file name, removing .. elements and checking that
684 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
685 on the system that has the referenced file system.
686 Widelinks are allowed if widelinks is true.
687 ********************************************************************/
689 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
699 BOOL relative = (*s != '/');
701 *dir2 = *wd = *base_name = *newname = 0;
706 /* can't have a leading .. */
707 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
709 DEBUG(3,("Illegal file name? (%s)\n",s));
719 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
721 /* remove any double slashes */
722 all_string_sub(s,"//","/",0);
724 pstrcpy(base_name,s);
725 p = strrchr_m(base_name,'/');
730 if (!vfs_GetWd(conn,wd))
732 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
736 if (vfs_ChDir(conn,dir) != 0)
738 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
742 if (!vfs_GetWd(conn,dir2))
744 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
749 if (p && (p != base_name))
752 if (strcmp(p+1,".")==0)
754 if (strcmp(p+1,"..")==0)
758 if (vfs_ChDir(conn,base_name) != 0)
761 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
765 if (!vfs_GetWd(conn,newname))
768 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
772 if (p && (p != base_name))
774 pstrcat(newname,"/");
775 pstrcat(newname,p+1);
779 size_t l = strlen(dir2);
780 if (dir2[l-1] == '/')
783 if (strncmp(newname,dir2,l) != 0)
786 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
792 if (newname[l] == '/')
793 pstrcpy(s,newname + l + 1);
795 pstrcpy(s,newname+l);
806 DEBUG(3,("reduced to %s\n",s));