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 */
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 vfs_init(connection_struct *conn)
161 if (*lp_vfsobj(SNUM(conn))) {
164 /* Loadable object file */
166 if (!vfs_init_custom(conn)) {
167 DEBUG(0, ("vfs_init: vfs_init_custom failed\n"));
173 DEBUG(0, ("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 a vfs file exists.
237 ********************************************************************/
239 BOOL vfs_file_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
248 if (vfs_stat(conn,fname,sbuf) != 0)
251 return(S_ISREG(sbuf->st_mode));
254 /****************************************************************************
255 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
256 ****************************************************************************/
258 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
262 while (total < byte_count)
264 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
267 if (ret == 0) return total;
276 return (ssize_t)total;
279 /****************************************************************************
280 Write data to a fd on the vfs.
281 ****************************************************************************/
283 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
290 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
292 if (ret == -1) return -1;
293 if (ret == 0) return total;
297 return (ssize_t)total;
300 /****************************************************************************
301 An allocate file space call using the vfs interface.
302 Allocates space for a file from a filedescriptor.
303 Returns 0 on success, -1 on failure.
304 ****************************************************************************/
306 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
310 struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
312 if (!lp_strict_allocate(SNUM(fsp->conn)))
313 return vfs_set_filelen(fsp, len);
315 release_level_2_oplocks_on_change(fsp);
318 * Actually try and commit the space on disk....
321 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
323 ret = vfs_fstat(fsp,fsp->fd,&st);
327 if (len == st.st_size)
330 if (len < st.st_size) {
331 /* Shrink - use ftruncate. */
333 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
334 fsp->fsp_name, (double)st.st_size ));
336 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
337 set_filelen_write_cache(fsp, len);
342 /* Grow - we need to write out the space.... */
344 static unsigned char zero_space[65536];
346 SMB_OFF_T start_pos = st.st_size;
347 SMB_OFF_T len_to_write = len - st.st_size;
350 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f\n",
351 fsp->fsp_name, (double)st.st_size ));
353 if ((retlen = vfs_ops->lseek(fsp, fsp->fd, start_pos, SEEK_SET)) != start_pos)
356 while ( len_to_write > 0) {
357 SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),len_to_write);
359 retlen = vfs_ops->write(fsp,fsp->fd,(const char *)zero_space,current_len_to_write);
361 /* Write fail - return to original size. */
362 int save_errno = errno;
363 fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, st.st_size);
365 DEBUG(10,("vfs_allocate_file_space: file %s, grow. write fail %s\n",
366 fsp->fsp_name, strerror(errno) ));
370 DEBUG(10,("vfs_allocate_file_space: file %s, grow. wrote %.0f\n",
371 fsp->fsp_name, (double)retlen ));
373 len_to_write -= retlen;
375 set_filelen_write_cache(fsp, len);
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 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
392 set_filelen_write_cache(fsp, len);
397 /****************************************************************************
398 Transfer some data (n bytes) between two file_struct's.
399 ****************************************************************************/
401 static files_struct *in_fsp;
402 static files_struct *out_fsp;
404 static ssize_t read_fn(int fd, void *buf, size_t len)
406 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
409 static ssize_t write_fn(int fd, const void *buf, size_t len)
411 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
414 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
419 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
422 /*******************************************************************
423 A vfs_readdir wrapper which just returns the file name.
424 ********************************************************************/
426 char *vfs_readdirname(connection_struct *conn, void *p)
434 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
445 #ifdef HAVE_BROKEN_READDIR
446 /* using /usr/ucb/cc is BAD */
453 /* VFS options not quite working yet */
457 /***************************************************************************
458 handle the interpretation of the vfs option parameter
459 *************************************************************************/
460 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
462 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
465 /* Create new vfs option */
467 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
468 if (new_option == NULL) {
472 ZERO_STRUCTP(new_option);
474 /* Get name and value */
476 new_option->name = strtok(pszParmValue, "=");
478 if (new_option->name == NULL) {
482 while(isspace(*new_option->name)) {
486 for (i = strlen(new_option->name); i > 0; i--) {
487 if (!isspace(new_option->name[i - 1])) break;
490 new_option->name[i] = '\0';
491 new_option->name = strdup(new_option->name);
493 new_option->value = strtok(NULL, "=");
495 if (new_option->value != NULL) {
497 while(isspace(*new_option->value)) {
501 for (i = strlen(new_option->value); i > 0; i--) {
502 if (!isspace(new_option->value[i - 1])) break;
505 new_option->value[i] = '\0';
506 new_option->value = strdup(new_option->value);
511 DLIST_ADD(*options, new_option);
519 /*******************************************************************
520 A wrapper for vfs_chdir().
521 ********************************************************************/
523 int vfs_ChDir(connection_struct *conn, char *path)
526 static pstring LastDir="";
528 if (strcsequal(path,"."))
531 if (*path == '/' && strcsequal(LastDir,path))
534 DEBUG(3,("vfs_ChDir to %s\n",path));
536 res = vfs_chdir(conn,path);
538 pstrcpy(LastDir,path);
542 /* number of list structures for a caching GetWd function. */
543 #define MAX_GETWDCACHE (50)
547 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
548 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
549 char *dos_path; /* The pathname in DOS format. */
551 } ino_list[MAX_GETWDCACHE];
553 extern BOOL use_getwd_cache;
555 /****************************************************************************
556 Prompte a ptr (to make it recently used)
557 ****************************************************************************/
559 static void array_promote(char *array,int elsize,int element)
565 p = (char *)malloc(elsize);
568 DEBUG(5,("array_promote: malloc fail\n"));
572 memcpy(p,array + element * elsize, elsize);
573 memmove(array + elsize,array,elsize*element);
574 memcpy(array,p,elsize);
578 /*******************************************************************
579 Return the absolute current directory path - given a UNIX pathname.
580 Note that this path is returned in DOS format, not UNIX
581 format. Note this can be called with conn == NULL.
582 ********************************************************************/
584 char *vfs_GetWd(connection_struct *conn, char *path)
587 static BOOL getwd_cache_init = False;
588 SMB_STRUCT_STAT st, st2;
593 if (!use_getwd_cache)
594 return(vfs_getwd(conn,path));
597 if (!getwd_cache_init)
599 getwd_cache_init = True;
600 for (i=0;i<MAX_GETWDCACHE;i++)
602 string_set(&ino_list[i].dos_path,"");
603 ino_list[i].valid = False;
607 /* Get the inode of the current directory, if this doesn't work we're
610 if (vfs_stat(conn, ".",&st) == -1)
612 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
613 return(vfs_getwd(conn,path));
617 for (i=0; i<MAX_GETWDCACHE; i++)
618 if (ino_list[i].valid)
621 /* If we have found an entry with a matching inode and dev number
622 then find the inode number for the directory in the cached string.
623 If this agrees with that returned by the stat for the current
624 directory then all is o.k. (but make sure it is a directory all
627 if (st.st_ino == ino_list[i].inode &&
628 st.st_dev == ino_list[i].dev)
630 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
632 if (st.st_ino == st2.st_ino &&
633 st.st_dev == st2.st_dev &&
634 (st2.st_mode & S_IFMT) == S_IFDIR)
636 pstrcpy (path, ino_list[i].dos_path);
638 /* promote it for future use */
639 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
644 /* If the inode is different then something's changed,
645 scrub the entry and start from scratch. */
646 ino_list[i].valid = False;
653 /* We don't have the information to hand so rely on traditional methods.
654 The very slow getcwd, which spawns a process on some systems, or the
655 not quite so bad getwd. */
657 if (!vfs_getwd(conn,s))
659 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
665 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
667 /* add it to the cache */
668 i = MAX_GETWDCACHE - 1;
669 string_set(&ino_list[i].dos_path,s);
670 ino_list[i].dev = st.st_dev;
671 ino_list[i].inode = st.st_ino;
672 ino_list[i].valid = True;
674 /* put it at the top of the list */
675 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
680 /*******************************************************************
681 Reduce a file name, removing .. elements and checking that
682 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
683 on the system that has the referenced file system.
684 Widelinks are allowed if widelinks is true.
685 ********************************************************************/
687 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
697 BOOL relative = (*s != '/');
699 *dir2 = *wd = *base_name = *newname = 0;
704 /* can't have a leading .. */
705 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
707 DEBUG(3,("Illegal file name? (%s)\n",s));
717 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
719 /* remove any double slashes */
720 all_string_sub(s,"//","/",0);
722 pstrcpy(base_name,s);
723 p = strrchr_m(base_name,'/');
728 if (!vfs_GetWd(conn,wd))
730 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
734 if (vfs_ChDir(conn,dir) != 0)
736 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
740 if (!vfs_GetWd(conn,dir2))
742 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
747 if (p && (p != base_name))
750 if (strcmp(p+1,".")==0)
752 if (strcmp(p+1,"..")==0)
756 if (vfs_ChDir(conn,base_name) != 0)
759 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
763 if (!vfs_GetWd(conn,newname))
766 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
770 if (p && (p != base_name))
772 pstrcat(newname,"/");
773 pstrcat(newname,p+1);
777 size_t l = strlen(dir2);
778 if (dir2[l-1] == '/')
781 if (strncmp(newname,dir2,l) != 0)
784 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
790 if (newname[l] == '/')
791 pstrcpy(s,newname + l + 1);
793 pstrcpy(s,newname+l);
804 DEBUG(3,("reduced to %s\n",s));