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 */
81 #if defined(HAVE_NO_ACLS)
90 /****************************************************************************
91 initialise default vfs hooks
92 ****************************************************************************/
94 static BOOL vfs_init_default(connection_struct *conn)
96 DEBUG(3, ("Initialising default vfs hooks\n"));
98 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
102 /****************************************************************************
103 initialise custom vfs hooks
104 ****************************************************************************/
107 static BOOL vfs_init_custom(connection_struct *conn)
109 int vfs_version = -1;
110 struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
112 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
113 lp_vfsobj(SNUM(conn))));
115 /* Open object file */
116 if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)),
117 RTLD_NOW | RTLD_GLOBAL)) == NULL) {
118 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), sys_dlerror()));
122 /* Get handle on vfs_init() symbol */
123 init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
125 if (init_fptr == NULL) {
126 DEBUG(0, ("No vfs_init() symbol found in %s\n",
127 lp_vfsobj(SNUM(conn))));
131 /* Initialise vfs_ops structure */
132 conn->vfs_ops = default_vfs_ops;
134 if ((ops = init_fptr(&vfs_version, &conn->vfs_ops)) == NULL) {
135 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
139 if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
140 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
141 vfs_version, SMB_VFS_INTERFACE_VERSION ));
145 if (ops != &conn->vfs_ops) {
146 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
153 /*****************************************************************
155 ******************************************************************/
157 BOOL smbd_vfs_init(connection_struct *conn)
159 if (*lp_vfsobj(SNUM(conn))) {
162 /* Loadable object file */
164 if (!vfs_init_custom(conn)) {
165 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
171 DEBUG(0, ("smbd_vfs_init: No libdl present - cannot use VFS objects\n"));
176 /* Normal share - initialise with disk access functions */
178 return vfs_init_default(conn);
181 /*******************************************************************
182 Check if directory exists.
183 ********************************************************************/
185 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
193 if (vfs_stat(conn,dname,st) != 0)
196 ret = S_ISDIR(st->st_mode);
203 /*******************************************************************
205 ********************************************************************/
206 char *vfs_getwd(connection_struct *conn, char *path)
208 return conn->vfs_ops.getwd(conn,path);
211 /*******************************************************************
213 ********************************************************************/
215 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
218 SMB_STRUCT_STAT sbuf;
220 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
222 * Check if high bits should have been set,
223 * then (if bits are missing): add them.
224 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
226 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
227 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
228 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
233 /*******************************************************************
234 Check if a vfs file exists.
235 ********************************************************************/
237 BOOL vfs_file_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
246 if (vfs_stat(conn,fname,sbuf) != 0)
249 return(S_ISREG(sbuf->st_mode));
252 /****************************************************************************
253 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
254 ****************************************************************************/
256 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
260 while (total < byte_count)
262 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
265 if (ret == 0) return total;
274 return (ssize_t)total;
277 /****************************************************************************
278 Write data to a fd on the vfs.
279 ****************************************************************************/
281 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
288 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
290 if (ret == -1) return -1;
291 if (ret == 0) return total;
295 return (ssize_t)total;
298 /****************************************************************************
299 An allocate file space call using the vfs interface.
300 Allocates space for a file from a filedescriptor.
301 Returns 0 on success, -1 on failure.
302 ****************************************************************************/
304 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
308 struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
310 if (!lp_strict_allocate(SNUM(fsp->conn)))
311 return vfs_set_filelen(fsp, len);
313 release_level_2_oplocks_on_change(fsp);
316 * Actually try and commit the space on disk....
319 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
321 ret = vfs_fstat(fsp,fsp->fd,&st);
325 if (len == st.st_size)
328 if (len < st.st_size) {
329 /* Shrink - use ftruncate. */
331 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
332 fsp->fsp_name, (double)st.st_size ));
334 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
335 set_filelen_write_cache(fsp, len);
340 /* Grow - we need to write out the space.... */
342 static unsigned char zero_space[65536];
344 SMB_OFF_T start_pos = st.st_size;
345 SMB_OFF_T len_to_write = len - st.st_size;
348 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f\n",
349 fsp->fsp_name, (double)st.st_size ));
351 if ((retlen = vfs_ops->lseek(fsp, fsp->fd, start_pos, SEEK_SET)) != start_pos)
354 while ( len_to_write > 0) {
355 SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),len_to_write);
357 retlen = vfs_ops->write(fsp,fsp->fd,(const char *)zero_space,current_len_to_write);
359 /* Write fail - return to original size. */
360 int save_errno = errno;
361 fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, st.st_size);
363 DEBUG(10,("vfs_allocate_file_space: file %s, grow. write fail %s\n",
364 fsp->fsp_name, strerror(errno) ));
368 DEBUG(10,("vfs_allocate_file_space: file %s, grow. wrote %.0f\n",
369 fsp->fsp_name, (double)retlen ));
371 len_to_write -= retlen;
373 set_filelen_write_cache(fsp, len);
378 /****************************************************************************
379 A vfs set_filelen call.
380 set the length of a file from a filedescriptor.
381 Returns 0 on success, -1 on failure.
382 ****************************************************************************/
384 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
388 release_level_2_oplocks_on_change(fsp);
389 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
390 set_filelen_write_cache(fsp, len);
395 /****************************************************************************
396 Transfer some data (n bytes) between two file_struct's.
397 ****************************************************************************/
399 static files_struct *in_fsp;
400 static files_struct *out_fsp;
402 static ssize_t read_fn(int fd, void *buf, size_t len)
404 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
407 static ssize_t write_fn(int fd, const void *buf, size_t len)
409 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
412 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
417 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
420 /*******************************************************************
421 A vfs_readdir wrapper which just returns the file name.
422 ********************************************************************/
424 char *vfs_readdirname(connection_struct *conn, void *p)
432 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
443 #ifdef HAVE_BROKEN_READDIR
444 /* using /usr/ucb/cc is BAD */
451 /* VFS options not quite working yet */
455 /***************************************************************************
456 handle the interpretation of the vfs option parameter
457 *************************************************************************/
458 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
460 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
463 /* Create new vfs option */
465 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
466 if (new_option == NULL) {
470 ZERO_STRUCTP(new_option);
472 /* Get name and value */
474 new_option->name = strtok(pszParmValue, "=");
476 if (new_option->name == NULL) {
480 while(isspace(*new_option->name)) {
484 for (i = strlen(new_option->name); i > 0; i--) {
485 if (!isspace(new_option->name[i - 1])) break;
488 new_option->name[i] = '\0';
489 new_option->name = strdup(new_option->name);
491 new_option->value = strtok(NULL, "=");
493 if (new_option->value != NULL) {
495 while(isspace(*new_option->value)) {
499 for (i = strlen(new_option->value); i > 0; i--) {
500 if (!isspace(new_option->value[i - 1])) break;
503 new_option->value[i] = '\0';
504 new_option->value = strdup(new_option->value);
509 DLIST_ADD(*options, new_option);
517 /*******************************************************************
518 A wrapper for vfs_chdir().
519 ********************************************************************/
521 int vfs_ChDir(connection_struct *conn, char *path)
524 static pstring LastDir="";
526 if (strcsequal(path,"."))
529 if (*path == '/' && strcsequal(LastDir,path))
532 DEBUG(3,("vfs_ChDir to %s\n",path));
534 res = vfs_chdir(conn,path);
536 pstrcpy(LastDir,path);
540 /* number of list structures for a caching GetWd function. */
541 #define MAX_GETWDCACHE (50)
545 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
546 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
547 char *dos_path; /* The pathname in DOS format. */
549 } ino_list[MAX_GETWDCACHE];
551 extern BOOL use_getwd_cache;
553 /****************************************************************************
554 Prompte a ptr (to make it recently used)
555 ****************************************************************************/
557 static void array_promote(char *array,int elsize,int element)
563 p = (char *)malloc(elsize);
566 DEBUG(5,("array_promote: malloc fail\n"));
570 memcpy(p,array + element * elsize, elsize);
571 memmove(array + elsize,array,elsize*element);
572 memcpy(array,p,elsize);
576 /*******************************************************************
577 Return the absolute current directory path - given a UNIX pathname.
578 Note that this path is returned in DOS format, not UNIX
579 format. Note this can be called with conn == NULL.
580 ********************************************************************/
582 char *vfs_GetWd(connection_struct *conn, char *path)
585 static BOOL getwd_cache_init = False;
586 SMB_STRUCT_STAT st, st2;
591 if (!use_getwd_cache)
592 return(vfs_getwd(conn,path));
595 if (!getwd_cache_init)
597 getwd_cache_init = True;
598 for (i=0;i<MAX_GETWDCACHE;i++)
600 string_set(&ino_list[i].dos_path,"");
601 ino_list[i].valid = False;
605 /* Get the inode of the current directory, if this doesn't work we're
608 if (vfs_stat(conn, ".",&st) == -1)
610 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
611 return(vfs_getwd(conn,path));
615 for (i=0; i<MAX_GETWDCACHE; i++)
616 if (ino_list[i].valid)
619 /* If we have found an entry with a matching inode and dev number
620 then find the inode number for the directory in the cached string.
621 If this agrees with that returned by the stat for the current
622 directory then all is o.k. (but make sure it is a directory all
625 if (st.st_ino == ino_list[i].inode &&
626 st.st_dev == ino_list[i].dev)
628 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
630 if (st.st_ino == st2.st_ino &&
631 st.st_dev == st2.st_dev &&
632 (st2.st_mode & S_IFMT) == S_IFDIR)
634 pstrcpy (path, ino_list[i].dos_path);
636 /* promote it for future use */
637 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
642 /* If the inode is different then something's changed,
643 scrub the entry and start from scratch. */
644 ino_list[i].valid = False;
651 /* We don't have the information to hand so rely on traditional methods.
652 The very slow getcwd, which spawns a process on some systems, or the
653 not quite so bad getwd. */
655 if (!vfs_getwd(conn,s))
657 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
663 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
665 /* add it to the cache */
666 i = MAX_GETWDCACHE - 1;
667 string_set(&ino_list[i].dos_path,s);
668 ino_list[i].dev = st.st_dev;
669 ino_list[i].inode = st.st_ino;
670 ino_list[i].valid = True;
672 /* put it at the top of the list */
673 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
678 /*******************************************************************
679 Reduce a file name, removing .. elements and checking that
680 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
681 on the system that has the referenced file system.
682 Widelinks are allowed if widelinks is true.
683 ********************************************************************/
685 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
695 BOOL relative = (*s != '/');
697 *dir2 = *wd = *base_name = *newname = 0;
702 /* can't have a leading .. */
703 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
705 DEBUG(3,("Illegal file name? (%s)\n",s));
715 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
717 /* remove any double slashes */
718 all_string_sub(s,"//","/",0);
720 pstrcpy(base_name,s);
721 p = strrchr_m(base_name,'/');
726 if (!vfs_GetWd(conn,wd))
728 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
732 if (vfs_ChDir(conn,dir) != 0)
734 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
738 if (!vfs_GetWd(conn,dir2))
740 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
745 if (p && (p != base_name))
748 if (strcmp(p+1,".")==0)
750 if (strcmp(p+1,"..")==0)
754 if (vfs_ChDir(conn,base_name) != 0)
757 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
761 if (!vfs_GetWd(conn,newname))
764 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
768 if (p && (p != base_name))
770 pstrcat(newname,"/");
771 pstrcat(newname,p+1);
775 size_t l = strlen(dir2);
776 if (dir2[l-1] == '/')
779 if (strncmp(newname,dir2,l) != 0)
782 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
788 if (newname[l] == '/')
789 pstrcpy(s,newname + l + 1);
791 pstrcpy(s,newname+l);
802 DEBUG(3,("reduced to %s\n",s));