add WITH_SENDFILE profiling data (from Pierre Belanger)
[Samba.git] / source / smbd / vfs.c
blobf94f917415e0488390b7331de586aabb04ef809a
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
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.
22 #include "includes.h"
24 /* Some structures to help us initialise the vfs operations table */
26 struct vfs_syminfo {
27 char *name;
28 void *fptr;
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 = {
37 /* Disk operations */
39 vfswrap_dummy_connect,
40 vfswrap_dummy_disconnect,
41 vfswrap_disk_free,
43 /* Directory operations */
45 vfswrap_opendir,
46 vfswrap_readdir,
47 vfswrap_mkdir,
48 vfswrap_rmdir,
49 vfswrap_closedir,
51 /* File operations */
53 vfswrap_open,
54 vfswrap_close,
55 vfswrap_read,
56 vfswrap_write,
57 vfswrap_lseek,
58 vfswrap_sendfile,
59 vfswrap_rename,
60 vfswrap_fsync,
61 vfswrap_stat,
62 vfswrap_fstat,
63 vfswrap_lstat,
64 vfswrap_unlink,
65 vfswrap_chmod,
66 vfswrap_fchmod,
67 vfswrap_chown,
68 vfswrap_fchown,
69 vfswrap_chdir,
70 vfswrap_getwd,
71 vfswrap_utime,
72 vfswrap_ftruncate,
73 vfswrap_lock,
74 vfswrap_symlink,
75 vfswrap_readlink,
76 vfswrap_link,
77 vfswrap_mknod,
78 vfswrap_realpath,
80 vfswrap_fget_nt_acl,
81 vfswrap_get_nt_acl,
82 vfswrap_fset_nt_acl,
83 vfswrap_set_nt_acl,
85 /* POSIX ACL operations. */
86 #if defined(HAVE_NO_ACLS)
87 NULL,
88 NULL,
89 #else
90 vfswrap_chmod_acl,
91 vfswrap_fchmod_acl,
92 #endif
93 vfswrap_sys_acl_get_entry,
94 vfswrap_sys_acl_get_tag_type,
95 vfswrap_sys_acl_get_permset,
96 vfswrap_sys_acl_get_qualifier,
97 vfswrap_sys_acl_get_file,
98 vfswrap_sys_acl_get_fd,
99 vfswrap_sys_acl_clear_perms,
100 vfswrap_sys_acl_add_perm,
101 vfswrap_sys_acl_to_text,
102 vfswrap_sys_acl_init,
103 vfswrap_sys_acl_create_entry,
104 vfswrap_sys_acl_set_tag_type,
105 vfswrap_sys_acl_set_qualifier,
106 vfswrap_sys_acl_set_permset,
107 vfswrap_sys_acl_valid,
108 vfswrap_sys_acl_set_file,
109 vfswrap_sys_acl_set_fd,
110 vfswrap_sys_acl_delete_def_file,
111 vfswrap_sys_acl_get_perm,
112 vfswrap_sys_acl_free_text,
113 vfswrap_sys_acl_free_acl,
114 vfswrap_sys_acl_free_qualifier
117 /****************************************************************************
118 initialise default vfs hooks
119 ****************************************************************************/
121 static BOOL vfs_init_default(connection_struct *conn)
123 DEBUG(3, ("Initialising default vfs hooks\n"));
125 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
126 return True;
129 /****************************************************************************
130 initialise custom vfs hooks
131 ****************************************************************************/
133 static BOOL vfs_init_custom(connection_struct *conn)
135 int vfs_version = -1;
136 struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
138 DEBUG(3, ("Initialising custom vfs hooks from %s\n", lp_vfsobj(SNUM(conn))));
140 /* Open object file */
142 if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) {
143 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), sys_dlerror()));
144 return False;
147 /* Get handle on vfs_init() symbol */
149 init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
151 if (init_fptr == NULL) {
152 DEBUG(0, ("No vfs_init() symbol found in %s\n", lp_vfsobj(SNUM(conn))));
153 return False;
156 /* Initialise vfs_ops structure */
158 conn->vfs_ops = default_vfs_ops;
160 if ((ops = init_fptr(&vfs_version, &default_vfs_ops)) == NULL) {
161 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
162 return False;
165 if (vfs_version != SMB_VFS_INTERFACE_VERSION) {
166 DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n",
167 vfs_version, SMB_VFS_INTERFACE_VERSION ));
168 return False;
171 if (ops != &conn->vfs_ops) {
172 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
175 return True;
178 /*****************************************************************
179 Generic VFS init.
180 ******************************************************************/
182 BOOL smbd_vfs_init(connection_struct *conn)
184 if (*lp_vfsobj(SNUM(conn))) {
185 /* Loadable object file */
187 if (!vfs_init_custom(conn)) {
188 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
189 return False;
192 return True;
195 /* Normal share - initialise with disk access functions */
197 return vfs_init_default(conn);
200 /*******************************************************************
201 Check if directory exists.
202 ********************************************************************/
204 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
206 SMB_STRUCT_STAT st2;
207 BOOL ret;
209 if (!st)
210 st = &st2;
212 if (vfs_stat(conn,dname,st) != 0)
213 return(False);
215 ret = S_ISDIR(st->st_mode);
216 if(!ret)
217 errno = ENOTDIR;
219 return ret;
222 /*******************************************************************
223 vfs mkdir wrapper that calls dos_to_unix.
224 ********************************************************************/
226 int vfs_mkdir(connection_struct *conn, char *const fname, mode_t mode)
228 int ret;
229 pstring name;
230 SMB_STRUCT_STAT sbuf;
232 pstrcpy(name,dos_to_unix_static(fname)); /* paranoia copy */
233 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
235 * Check if high bits should have been set,
236 * then (if bits are missing): add them.
237 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
239 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
240 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
241 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
243 return ret;
246 /*******************************************************************
247 vfs getwd wrapper that calls dos_to_unix.
248 ********************************************************************/
250 char *vfs_getwd(connection_struct *conn, char *unix_path)
252 char *wd;
253 wd = conn->vfs_ops.getwd(conn,unix_path);
254 if (wd)
255 unix_to_dos(wd);
256 return wd;
259 /*******************************************************************
260 Check if an object exists in the vfs.
261 ********************************************************************/
263 BOOL vfs_object_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
265 SMB_STRUCT_STAT st;
267 if (!sbuf)
268 sbuf = &st;
270 ZERO_STRUCTP(sbuf);
272 if (vfs_stat(conn,fname,sbuf) == -1)
273 return(False);
274 return True;
277 /*******************************************************************
278 Check if a file exists in the vfs.
279 ********************************************************************/
281 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
283 SMB_STRUCT_STAT st;
285 if (!sbuf)
286 sbuf = &st;
288 ZERO_STRUCTP(sbuf);
290 if (vfs_stat(conn,fname,sbuf) == -1)
291 return False;
292 return(S_ISREG(sbuf->st_mode));
295 /****************************************************************************
296 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
297 ****************************************************************************/
299 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
301 size_t total=0;
303 while (total < byte_count)
305 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
306 byte_count - total);
308 if (ret == 0) return total;
309 if (ret == -1) {
310 if (errno == EINTR)
311 continue;
312 else
313 return -1;
315 total += ret;
317 return (ssize_t)total;
320 /****************************************************************************
321 Write data to a fd on the vfs.
322 ****************************************************************************/
324 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
326 size_t total=0;
327 ssize_t ret;
329 while (total < N) {
330 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
332 if (ret == -1)
333 return -1;
334 if (ret == 0)
335 return total;
337 total += ret;
340 return (ssize_t)total;
343 /****************************************************************************
344 An allocate file space call using the vfs interface.
345 Allocates space for a file from a filedescriptor.
346 Returns 0 on success, -1 on failure.
347 ****************************************************************************/
349 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
351 int ret;
352 SMB_STRUCT_STAT st;
353 connection_struct *conn = fsp->conn;
354 struct vfs_ops *vfs_ops = &conn->vfs_ops;
355 SMB_OFF_T space_avail;
356 SMB_BIG_UINT bsize,dfree,dsize;
358 release_level_2_oplocks_on_change(fsp);
361 * Actually try and commit the space on disk....
364 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
366 ret = vfs_fstat(fsp,fsp->fd,&st);
367 if (ret == -1)
368 return ret;
370 if (len == st.st_size)
371 return 0;
373 if (len < st.st_size) {
374 /* Shrink - use ftruncate. */
376 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
377 fsp->fsp_name, (double)st.st_size ));
379 flush_write_cache(fsp, SIZECHANGE_FLUSH);
380 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
381 set_filelen_write_cache(fsp, len);
383 return ret;
386 /* Grow - we need to test if we have enough space. */
388 if (!lp_strict_allocate(SNUM(fsp->conn)))
389 return 0;
391 len -= st.st_size;
392 len /= 1024; /* Len is now number of 1k blocks needed. */
393 space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
395 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
396 fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
398 if (len > space_avail) {
399 errno = ENOSPC;
400 return -1;
403 return 0;
406 /****************************************************************************
407 A vfs set_filelen call.
408 set the length of a file from a filedescriptor.
409 Returns 0 on success, -1 on failure.
410 ****************************************************************************/
412 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
414 int ret;
416 release_level_2_oplocks_on_change(fsp);
417 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
418 flush_write_cache(fsp, SIZECHANGE_FLUSH);
419 if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
420 set_filelen_write_cache(fsp, len);
422 return ret;
425 /****************************************************************************
426 Transfer some data (n bytes) between two file_struct's.
427 ****************************************************************************/
429 static files_struct *in_fsp;
430 static files_struct *out_fsp;
432 static ssize_t read_fn(int fd, void *buf, size_t len)
434 return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
437 static ssize_t write_fn(int fd, const void *buf, size_t len)
439 return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
442 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
444 in_fsp = in;
445 out_fsp = out;
447 return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
450 /*******************************************************************
451 A vfs_readdir wrapper which just returns the file name.
452 ********************************************************************/
454 char *vfs_readdirname(connection_struct *conn, void *p)
456 struct dirent *ptr;
457 char *dname;
459 if (!p)
460 return(NULL);
462 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
463 if (!ptr)
464 return(NULL);
466 dname = ptr->d_name;
468 #ifdef NEXT2
469 if (telldir(p) < 0)
470 return(NULL);
471 #endif
473 #ifdef HAVE_BROKEN_READDIR
474 /* using /usr/ucb/cc is BAD */
475 dname = dname - 2;
476 #endif
479 static pstring buf;
480 memcpy(buf, dname, NAMLEN(ptr)+1);
481 unix_to_dos(buf);
482 dname = buf;
485 return(dname);
488 /* VFS options not quite working yet */
490 #if 0
492 /***************************************************************************
493 handle the interpretation of the vfs option parameter
494 *************************************************************************/
495 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
497 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
498 int i;
500 /* Create new vfs option */
502 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
503 if (new_option == NULL) {
504 return False;
507 ZERO_STRUCTP(new_option);
509 /* Get name and value */
511 new_option->name = strtok(pszParmValue, "=");
513 if (new_option->name == NULL) {
514 return False;
517 while(isspace(*new_option->name)) {
518 new_option->name++;
521 for (i = strlen(new_option->name); i > 0; i--) {
522 if (!isspace(new_option->name[i - 1])) break;
525 new_option->name[i] = '\0';
526 new_option->name = strdup(new_option->name);
528 new_option->value = strtok(NULL, "=");
530 if (new_option->value != NULL) {
532 while(isspace(*new_option->value)) {
533 new_option->value++;
536 for (i = strlen(new_option->value); i > 0; i--) {
537 if (!isspace(new_option->value[i - 1])) break;
540 new_option->value[i] = '\0';
541 new_option->value = strdup(new_option->value);
544 /* Add to list */
546 DLIST_ADD(*options, new_option);
548 return True;
551 #endif
554 /*******************************************************************
555 A wrapper for vfs_chdir().
556 ********************************************************************/
558 int vfs_ChDir(connection_struct *conn, const char *path)
560 int res;
561 static pstring LastDir="";
563 if (strcsequal(path,"."))
564 return(0);
566 if (*path == '/' && strcsequal(LastDir,path))
567 return(0);
569 DEBUG(3,("vfs_ChDir to %s\n",path));
571 res = vfs_chdir(conn,path);
572 if (!res)
573 pstrcpy(LastDir,path);
574 return(res);
577 /* number of list structures for a caching GetWd function. */
578 #define MAX_GETWDCACHE (50)
580 struct {
581 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
582 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
583 char *dos_path; /* The pathname in DOS format. */
584 BOOL valid;
585 } ino_list[MAX_GETWDCACHE];
587 extern BOOL use_getwd_cache;
589 /****************************************************************************
590 Prompte a ptr (to make it recently used)
591 ****************************************************************************/
593 static void array_promote(char *array,int elsize,int element)
595 char *p;
596 if (element == 0)
597 return;
599 p = (char *)malloc(elsize);
601 if (!p) {
602 DEBUG(5,("array_promote: malloc fail\n"));
603 return;
606 memcpy(p,array + element * elsize, elsize);
607 memmove(array + elsize,array,elsize*element);
608 memcpy(array,p,elsize);
609 SAFE_FREE(p);
612 /*******************************************************************
613 Return the absolute current directory path - given a UNIX pathname.
614 Note that this path is returned in DOS format, not UNIX
615 format. Note this can be called with conn == NULL.
616 ********************************************************************/
618 char *vfs_GetWd(connection_struct *conn, char *path)
620 pstring s;
621 static BOOL getwd_cache_init = False;
622 SMB_STRUCT_STAT st, st2;
623 int i;
625 *s = 0;
627 if (!use_getwd_cache)
628 return(vfs_getwd(conn,path));
630 /* init the cache */
631 if (!getwd_cache_init) {
632 getwd_cache_init = True;
633 for (i=0;i<MAX_GETWDCACHE;i++) {
634 string_set(&ino_list[i].dos_path,"");
635 ino_list[i].valid = False;
639 /* Get the inode of the current directory, if this doesn't work we're
640 in trouble :-) */
642 if (vfs_stat(conn, ".",&st) == -1) {
643 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
644 return(vfs_getwd(conn,path));
648 for (i=0; i<MAX_GETWDCACHE; i++) {
649 if (ino_list[i].valid) {
651 /* If we have found an entry with a matching inode and dev number
652 then find the inode number for the directory in the cached string.
653 If this agrees with that returned by the stat for the current
654 directory then all is o.k. (but make sure it is a directory all
655 the same...) */
657 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
658 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
659 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
660 (st2.st_mode & S_IFMT) == S_IFDIR) {
661 pstrcpy (path, ino_list[i].dos_path);
663 /* promote it for future use */
664 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
665 return (path);
667 } else {
668 /* If the inode is different then something's changed,
669 scrub the entry and start from scratch. */
670 ino_list[i].valid = False;
677 /* We don't have the information to hand so rely on traditional methods.
678 The very slow getcwd, which spawns a process on some systems, or the
679 not quite so bad getwd. */
681 if (!vfs_getwd(conn,s)) {
682 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
683 return (NULL);
686 pstrcpy(path,s);
688 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
690 /* add it to the cache */
691 i = MAX_GETWDCACHE - 1;
692 string_set(&ino_list[i].dos_path,s);
693 ino_list[i].dev = st.st_dev;
694 ino_list[i].inode = st.st_ino;
695 ino_list[i].valid = True;
697 /* put it at the top of the list */
698 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
700 return (path);
703 /*******************************************************************
704 Reduce a file name, removing .. elements and checking that
705 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
706 on the system that has the referenced file system.
707 Widelinks are allowed if widelinks is true.
708 ********************************************************************/
710 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
712 #ifndef REDUCE_PATHS
713 return True;
714 #else
715 pstring dir2;
716 pstring wd;
717 pstring base_name;
718 pstring newname;
719 char *p=NULL;
720 BOOL relative = (*s != '/');
722 *dir2 = *wd = *base_name = *newname = 0;
724 if (widelinks) {
725 unix_clean_name(s);
726 /* can't have a leading .. */
727 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
728 DEBUG(3,("Illegal file name? (%s)\n",s));
729 return(False);
732 if (strlen(s) == 0)
733 pstrcpy(s,"./");
735 return(True);
738 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
740 /* remove any double slashes */
741 all_string_sub(s,"//","/",0);
743 pstrcpy(base_name,s);
744 p = strrchr(base_name,'/');
746 if (!p)
747 return(True);
749 if (!vfs_GetWd(conn,wd)) {
750 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
751 return(False);
754 if (vfs_ChDir(conn,dir) != 0) {
755 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
756 return(False);
759 if (!vfs_GetWd(conn,dir2)) {
760 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
761 vfs_ChDir(conn,wd);
762 return(False);
765 if (p && (p != base_name)) {
766 *p = 0;
767 if (strcmp(p+1,".")==0)
768 p[1]=0;
769 if (strcmp(p+1,"..")==0)
770 *p = '/';
773 if (vfs_ChDir(conn,base_name) != 0) {
774 vfs_ChDir(conn,wd);
775 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
776 return(False);
779 if (!vfs_GetWd(conn,newname)) {
780 vfs_ChDir(conn,wd);
781 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
782 return(False);
785 if (p && (p != base_name)) {
786 pstrcat(newname,"/");
787 pstrcat(newname,p+1);
791 size_t l = strlen(dir2);
792 if (dir2[l-1] == '/')
793 l--;
795 if (strncmp(newname,dir2,l) != 0) {
796 vfs_ChDir(conn,wd);
797 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
798 return(False);
801 if (relative) {
802 if (newname[l] == '/')
803 pstrcpy(s,newname + l + 1);
804 else
805 pstrcpy(s,newname+l);
806 } else
807 pstrcpy(s,newname);
810 vfs_ChDir(conn,wd);
812 if (strlen(s) == 0)
813 pstrcpy(s,"./");
815 DEBUG(3,("reduced to %s\n",s));
816 return(True);
817 #endif