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 */
79 /****************************************************************************
80 initialise default vfs hooks
81 ****************************************************************************/
82 int vfs_init_default(connection_struct *conn)
84 DEBUG(3, ("Initialising default vfs hooks\n"));
86 memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
90 /****************************************************************************
91 initialise custom vfs hooks
92 ****************************************************************************/
95 BOOL vfs_init_custom(connection_struct *conn)
97 struct vfs_ops *ops, *(*fptr)(struct vfs_options *options);
99 DEBUG(3, ("Initialising custom vfs hooks from %s\n",
100 lp_vfsobj(SNUM(conn))));
102 /* Open object file */
104 if ((conn->dl_handle = dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) {
105 DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), dlerror()));
109 /* Get handle on vfs_init() symbol */
111 fptr = (struct vfs_ops *(*)(struct vfs_options *))
112 dlsym(conn->dl_handle, "vfs_init");
115 DEBUG(0, ("No vfs_init() symbol found in %s\n",
116 lp_vfsobj(SNUM(conn))));
120 /* Initialise vfs_ops structure */
122 if ((ops = fptr(NULL)) == NULL) {
123 DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
127 /* Fill in unused operations with default (disk based) ones.
128 There's probably a neater way to do this then a whole bunch of
131 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
133 if (conn->vfs_ops.connect == NULL) {
134 conn->vfs_ops.connect = default_vfs_ops.connect;
137 if (conn->vfs_ops.disconnect == NULL) {
138 conn->vfs_ops.disconnect = default_vfs_ops.disconnect;
141 if (conn->vfs_ops.disk_free == NULL) {
142 conn->vfs_ops.disk_free = default_vfs_ops.disk_free;
145 if (conn->vfs_ops.opendir == NULL) {
146 conn->vfs_ops.opendir = default_vfs_ops.opendir;
149 if (conn->vfs_ops.readdir == NULL) {
150 conn->vfs_ops.readdir = default_vfs_ops.readdir;
153 if (conn->vfs_ops.mkdir == NULL) {
154 conn->vfs_ops.mkdir = default_vfs_ops.mkdir;
157 if (conn->vfs_ops.rmdir == NULL) {
158 conn->vfs_ops.rmdir = default_vfs_ops.rmdir;
161 if (conn->vfs_ops.closedir == NULL) {
162 conn->vfs_ops.closedir = default_vfs_ops.closedir;
165 if (conn->vfs_ops.open == NULL) {
166 conn->vfs_ops.open = default_vfs_ops.open;
169 if (conn->vfs_ops.close == NULL) {
170 conn->vfs_ops.close = default_vfs_ops.close;
173 if (conn->vfs_ops.read == NULL) {
174 conn->vfs_ops.read = default_vfs_ops.read;
177 if (conn->vfs_ops.write == NULL) {
178 conn->vfs_ops.write = default_vfs_ops.write;
181 if (conn->vfs_ops.lseek == NULL) {
182 conn->vfs_ops.lseek = default_vfs_ops.lseek;
185 if (conn->vfs_ops.rename == NULL) {
186 conn->vfs_ops.rename = default_vfs_ops.rename;
189 if (conn->vfs_ops.fsync == NULL) {
190 conn->vfs_ops.fsync = default_vfs_ops.fsync;
193 if (conn->vfs_ops.stat == NULL) {
194 conn->vfs_ops.stat = default_vfs_ops.stat;
197 if (conn->vfs_ops.fstat == NULL) {
198 conn->vfs_ops.fstat = default_vfs_ops.fstat;
201 if (conn->vfs_ops.lstat == NULL) {
202 conn->vfs_ops.lstat = default_vfs_ops.lstat;
205 if (conn->vfs_ops.unlink == NULL) {
206 conn->vfs_ops.unlink = default_vfs_ops.unlink;
209 if (conn->vfs_ops.chmod == NULL) {
210 conn->vfs_ops.chmod = default_vfs_ops.chmod;
213 if (conn->vfs_ops.chown == NULL) {
214 conn->vfs_ops.chown = default_vfs_ops.chown;
217 if (conn->vfs_ops.chdir == NULL) {
218 conn->vfs_ops.chdir = default_vfs_ops.chdir;
221 if (conn->vfs_ops.getwd == NULL) {
222 conn->vfs_ops.getwd = default_vfs_ops.getwd;
225 if (conn->vfs_ops.utime == NULL) {
226 conn->vfs_ops.utime = default_vfs_ops.utime;
229 if (conn->vfs_ops.ftruncate == NULL) {
230 conn->vfs_ops.ftruncate = default_vfs_ops.ftruncate;
233 if (conn->vfs_ops.lock == NULL) {
234 conn->vfs_ops.lock = default_vfs_ops.lock;
237 if (conn->vfs_ops.fget_nt_acl == NULL) {
238 conn->vfs_ops.fget_nt_acl = default_vfs_ops.fget_nt_acl;
241 if (conn->vfs_ops.get_nt_acl == NULL) {
242 conn->vfs_ops.get_nt_acl = default_vfs_ops.get_nt_acl;
245 if (conn->vfs_ops.fset_nt_acl == NULL) {
246 conn->vfs_ops.fset_nt_acl = default_vfs_ops.fset_nt_acl;
249 if (conn->vfs_ops.set_nt_acl == NULL) {
250 conn->vfs_ops.set_nt_acl = default_vfs_ops.set_nt_acl;
257 /*******************************************************************
258 Check if directory exists.
259 ********************************************************************/
261 BOOL vfs_directory_exist(connection_struct *conn, char *dname, SMB_STRUCT_STAT *st)
269 if (vfs_stat(conn,dname,st) != 0)
272 ret = S_ISDIR(st->st_mode);
279 /*******************************************************************
280 vfs mkdir wrapper that calls dos_to_unix.
281 ********************************************************************/
283 int vfs_mkdir(connection_struct *conn, char *fname, mode_t mode)
287 SMB_STRUCT_STAT sbuf;
289 pstrcpy(name,dos_to_unix(fname,False)); /* paranoia copy */
290 if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
292 * Check if high bits should have been set,
293 * then (if bits are missing): add them.
294 * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
296 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
297 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
298 vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
303 /*******************************************************************
304 vfs getwd wrapper that calls dos_to_unix.
305 ********************************************************************/
307 char *vfs_getwd(connection_struct *conn, char *unix_path)
310 wd = conn->vfs_ops.getwd(conn,unix_path);
312 unix_to_dos(wd, True);
316 /*******************************************************************
317 Check if a vfs file exists.
318 ********************************************************************/
320 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
329 if (vfs_stat(conn,fname,sbuf) != 0)
332 return(S_ISREG(sbuf->st_mode));
335 /****************************************************************************
336 Write data to a fd on the vfs.
337 ****************************************************************************/
339 ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
346 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
348 if (ret == -1) return -1;
349 if (ret == 0) return total;
353 return (ssize_t)total;
356 /****************************************************************************
357 Transfer some data between two file_struct's.
358 ****************************************************************************/
360 SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp,
361 int out_fd, files_struct *out_fsp,
362 SMB_OFF_T n, char *header, int headlen, int align)
364 static char *buf=NULL;
369 DEBUG(4,("vfs_transfer_file n=%.0f (head=%d) called\n",(double)n,headlen));
371 /* Check we have at least somewhere to read from */
373 SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
376 size = lp_readsize();
377 size = MAX(size,1024);
380 while (!buf && size>0) {
381 buf = (char *)Realloc(buf,size+8);
386 DEBUG(0,("Can't allocate transfer buffer!\n"));
390 abuf = buf + (align%8);
397 int s = (int)MIN(n,(SMB_OFF_T)size);
402 if (header && (headlen >= MIN(s,1024))) {
412 if (header && headlen > 0)
414 ret = MIN(headlen,size);
415 memcpy(buf1,header,ret);
418 if (headlen <= 0) header = NULL;
423 in_fsp->conn->vfs_ops.read(in_fsp,in_fsp->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
428 ret2 = out_fsp->conn->vfs_ops.write(out_fsp,out_fsp->fd,buf1,ret);
430 ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
433 if (ret2 > 0) total += ret2;
434 /* if we can't write then dump excess data */
436 vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
438 if (ret <= 0 || ret2 != ret)
445 /*******************************************************************
446 A vfs_readdir wrapper which just returns the file name.
447 ********************************************************************/
449 char *vfs_readdirname(connection_struct *conn, void *p)
457 ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
468 #ifdef HAVE_BROKEN_READDIR
469 /* using /usr/ucb/cc is BAD */
475 memcpy(buf, dname, NAMLEN(ptr)+1);
476 unix_to_dos(buf, True);
483 /* VFS options not quite working yet */
487 /***************************************************************************
488 handle the interpretation of the vfs option parameter
489 *************************************************************************/
490 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
492 struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
495 /* Create new vfs option */
497 new_option = (struct vfs_options *)malloc(sizeof(*new_option));
498 if (new_option == NULL) {
502 ZERO_STRUCTP(new_option);
504 /* Get name and value */
506 new_option->name = strtok(pszParmValue, "=");
508 if (new_option->name == NULL) {
512 while(isspace(*new_option->name)) {
516 for (i = strlen(new_option->name); i > 0; i--) {
517 if (!isspace(new_option->name[i - 1])) break;
520 new_option->name[i] = '\0';
521 new_option->name = strdup(new_option->name);
523 new_option->value = strtok(NULL, "=");
525 if (new_option->value != NULL) {
527 while(isspace(*new_option->value)) {
531 for (i = strlen(new_option->value); i > 0; i--) {
532 if (!isspace(new_option->value[i - 1])) break;
535 new_option->value[i] = '\0';
536 new_option->value = strdup(new_option->value);
541 DLIST_ADD(*options, new_option);
549 /*******************************************************************
550 A wrapper for vfs_chdir().
551 ********************************************************************/
553 int vfs_ChDir(connection_struct *conn, char *path)
556 static pstring LastDir="";
558 if (strcsequal(path,"."))
561 if (*path == '/' && strcsequal(LastDir,path))
564 DEBUG(3,("vfs_ChDir to %s\n",path));
566 res = vfs_chdir(conn,path);
568 pstrcpy(LastDir,path);
572 /* number of list structures for a caching GetWd function. */
573 #define MAX_GETWDCACHE (50)
577 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
578 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
579 char *dos_path; /* The pathname in DOS format. */
581 } ino_list[MAX_GETWDCACHE];
583 extern BOOL use_getwd_cache;
585 /****************************************************************************
586 Prompte a ptr (to make it recently used)
587 ****************************************************************************/
589 static void array_promote(char *array,int elsize,int element)
595 p = (char *)malloc(elsize);
598 DEBUG(5,("array_promote: malloc fail\n"));
602 memcpy(p,array + element * elsize, elsize);
603 memmove(array + elsize,array,elsize*element);
604 memcpy(array,p,elsize);
608 /*******************************************************************
609 Return the absolute current directory path - given a UNIX pathname.
610 Note that this path is returned in DOS format, not UNIX
611 format. Note this can be called with conn == NULL.
612 ********************************************************************/
614 char *vfs_GetWd(connection_struct *conn, char *path)
617 static BOOL getwd_cache_init = False;
618 SMB_STRUCT_STAT st, st2;
623 if (!use_getwd_cache)
624 return(vfs_getwd(conn,path));
627 if (!getwd_cache_init)
629 getwd_cache_init = True;
630 for (i=0;i<MAX_GETWDCACHE;i++)
632 string_set(&ino_list[i].dos_path,"");
633 ino_list[i].valid = False;
637 /* Get the inode of the current directory, if this doesn't work we're
640 if (vfs_stat(conn, ".",&st) == -1)
642 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
643 return(vfs_getwd(conn,path));
647 for (i=0; i<MAX_GETWDCACHE; i++)
648 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
657 if (st.st_ino == ino_list[i].inode &&
658 st.st_dev == ino_list[i].dev)
660 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
662 if (st.st_ino == st2.st_ino &&
663 st.st_dev == st2.st_dev &&
664 (st2.st_mode & S_IFMT) == S_IFDIR)
666 pstrcpy (path, ino_list[i].dos_path);
668 /* promote it for future use */
669 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
674 /* If the inode is different then something's changed,
675 scrub the entry and start from scratch. */
676 ino_list[i].valid = False;
683 /* We don't have the information to hand so rely on traditional methods.
684 The very slow getcwd, which spawns a process on some systems, or the
685 not quite so bad getwd. */
687 if (!vfs_getwd(conn,s))
689 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
695 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
697 /* add it to the cache */
698 i = MAX_GETWDCACHE - 1;
699 string_set(&ino_list[i].dos_path,s);
700 ino_list[i].dev = st.st_dev;
701 ino_list[i].inode = st.st_ino;
702 ino_list[i].valid = True;
704 /* put it at the top of the list */
705 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
710 /*******************************************************************
711 Reduce a file name, removing .. elements and checking that
712 it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
713 on the system that has the referenced file system.
714 Widelinks are allowed if widelinks is true.
715 ********************************************************************/
717 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
727 BOOL relative = (*s != '/');
729 *dir2 = *wd = *base_name = *newname = 0;
734 /* can't have a leading .. */
735 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
737 DEBUG(3,("Illegal file name? (%s)\n",s));
747 DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
749 /* remove any double slashes */
750 all_string_sub(s,"//","/",0);
752 pstrcpy(base_name,s);
753 p = strrchr(base_name,'/');
758 if (!vfs_GetWd(conn,wd))
760 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
764 if (vfs_ChDir(conn,dir) != 0)
766 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
770 if (!vfs_GetWd(conn,dir2))
772 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
777 if (p && (p != base_name))
780 if (strcmp(p+1,".")==0)
782 if (strcmp(p+1,"..")==0)
786 if (vfs_ChDir(conn,base_name) != 0)
789 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
793 if (!vfs_GetWd(conn,newname))
796 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
800 if (p && (p != base_name))
802 pstrcat(newname,"/");
803 pstrcat(newname,p+1);
807 size_t l = strlen(dir2);
808 if (dir2[l-1] == '/')
811 if (strncmp(newname,dir2,l) != 0)
814 DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
820 if (newname[l] == '/')
821 pstrcpy(s,newname + l + 1);
823 pstrcpy(s,newname+l);
834 DEBUG(3,("reduced to %s\n",s));