Getting ready to add UNIX extensions in HEAD also.
[ira/wip.git] / source3 / smbd / vfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    VFS initialisation and support functions
5    Copyright (C) Tim Potter 1999
6
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.
11
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.
16
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.
20 */
21
22 #include "includes.h"
23
24 /* Some structures to help us initialise the vfs operations table */
25
26 struct vfs_syminfo {
27         char *name;
28         void *fptr;
29 };
30
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. */
34
35 static struct vfs_ops default_vfs_ops = {
36
37         /* Disk operations */
38
39         vfswrap_dummy_connect,
40         vfswrap_dummy_disconnect,
41         vfswrap_disk_free,
42
43         /* Directory operations */
44
45         vfswrap_opendir,
46         vfswrap_readdir,
47         vfswrap_mkdir,
48         vfswrap_rmdir,
49         vfswrap_closedir,
50
51         /* File operations */
52
53         vfswrap_open,
54         vfswrap_close,
55         vfswrap_read,
56         vfswrap_write,
57         vfswrap_lseek,
58         vfswrap_rename,
59         vfswrap_fsync,
60         vfswrap_stat,
61         vfswrap_fstat,
62         vfswrap_lstat,
63         vfswrap_unlink,
64         vfswrap_chmod,
65         vfswrap_fchmod,
66         vfswrap_chown,
67         vfswrap_fchown,
68         vfswrap_chdir,
69         vfswrap_getwd,
70         vfswrap_utime,
71         vfswrap_ftruncate,
72         vfswrap_lock,
73         vfswrap_symlink,
74         vfswrap_readlink,
75         vfswrap_link,
76         vfswrap_mknod,
77
78         vfswrap_fget_nt_acl,
79         vfswrap_get_nt_acl,
80         vfswrap_fset_nt_acl,
81         vfswrap_set_nt_acl,
82
83 #if defined(HAVE_NO_ACLS)
84         NULL,
85         NULL
86 #else
87         vfswrap_chmod_acl,
88         vfswrap_fchmod_acl
89 #endif
90 };
91
92 /****************************************************************************
93   initialise default vfs hooks
94 ****************************************************************************/
95
96 static BOOL vfs_init_default(connection_struct *conn)
97 {
98     DEBUG(3, ("Initialising default vfs hooks\n"));
99
100     memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
101     return True;
102 }
103
104 /****************************************************************************
105   initialise custom vfs hooks
106 ****************************************************************************/
107
108 #ifdef HAVE_LIBDL
109 static BOOL vfs_init_custom(connection_struct *conn)
110 {
111         int vfs_version = -1;
112         struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *);
113
114         DEBUG(3, ("Initialising custom vfs hooks from %s\n",
115                   lp_vfsobj(SNUM(conn))));
116
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()));
121                 return False;
122         }
123
124         /* Get handle on vfs_init() symbol */
125         init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init");
126
127         if (init_fptr == NULL) {
128                 DEBUG(0, ("No vfs_init() symbol found in %s\n",
129                           lp_vfsobj(SNUM(conn))));
130                 return False;
131         }
132
133         /* Initialise vfs_ops structure */
134         conn->vfs_ops = default_vfs_ops;
135
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))));
138                 return False;
139         }
140         
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 ));
144                 return False;
145         }
146         
147         if (ops != &conn->vfs_ops) {
148                 memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops));
149         }
150
151         return True;
152 }
153 #endif
154
155 /*****************************************************************
156  Generic VFS init.
157 ******************************************************************/
158
159 BOOL smbd_vfs_init(connection_struct *conn)
160 {
161         if (*lp_vfsobj(SNUM(conn))) {
162 #ifdef HAVE_LIBDL
163  
164                 /* Loadable object file */
165  
166                 if (!vfs_init_custom(conn)) {
167                         DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n"));
168                         return False;
169                 }
170
171                 return True;
172 #else
173                 DEBUG(0, ("smbd_vfs_init: No libdl present - cannot use VFS objects\n"));
174                 return False;
175 #endif
176         }
177  
178         /* Normal share - initialise with disk access functions */
179  
180         return vfs_init_default(conn);
181 }
182
183 /*******************************************************************
184  Check if directory exists.
185 ********************************************************************/
186
187 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
188 {
189         SMB_STRUCT_STAT st2;
190         BOOL ret;
191
192         if (!st)
193                 st = &st2;
194
195         if (vfs_stat(conn,dname,st) != 0)
196                 return(False);
197
198         ret = S_ISDIR(st->st_mode);
199         if(!ret)
200                 errno = ENOTDIR;
201
202         return ret;
203 }
204
205 /*******************************************************************
206  vfs getwd wrapper 
207 ********************************************************************/
208 char *vfs_getwd(connection_struct *conn, char *path)
209 {
210         return conn->vfs_ops.getwd(conn,path);
211 }
212
213 /*******************************************************************
214  vfs mkdir wrapper 
215 ********************************************************************/
216
217 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
218 {
219         int ret;
220         SMB_STRUCT_STAT sbuf;
221
222         if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
223                 /*
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.
227                  */
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));
231         }
232         return ret;
233 }
234
235 /*******************************************************************
236  Check if an object exists in the vfs.
237 ********************************************************************/
238
239 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
240 {
241         SMB_STRUCT_STAT st;
242
243         if (!sbuf)
244                 sbuf = &st;
245
246         ZERO_STRUCTP(sbuf);
247
248         if (vfs_stat(conn,fname,sbuf) == -1)
249                 return(False);
250         return True;
251 }
252
253 /*******************************************************************
254  Check if a file exists in the vfs.
255 ********************************************************************/
256
257 BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
258 {
259         SMB_STRUCT_STAT st;
260
261         if (!sbuf)
262                 sbuf = &st;
263
264         ZERO_STRUCTP(sbuf);
265
266         if (vfs_stat(conn,fname,sbuf) == -1)
267                 return False;
268         return(S_ISREG(sbuf->st_mode));
269 }
270
271 /****************************************************************************
272  Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
273 ****************************************************************************/
274
275 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
276 {
277         size_t total=0;
278
279         while (total < byte_count)
280         {
281                 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
282                                                                                           byte_count - total);
283
284                 if (ret == 0) return total;
285                 if (ret == -1) {
286                         if (errno == EINTR)
287                                 continue;
288                         else
289                                 return -1;
290                 }
291                 total += ret;
292         }
293         return (ssize_t)total;
294 }
295
296 /****************************************************************************
297  Write data to a fd on the vfs.
298 ****************************************************************************/
299
300 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
301 {
302         size_t total=0;
303         ssize_t ret;
304
305         while (total < N) {
306                 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
307
308                 if (ret == -1)
309                         return -1;
310                 if (ret == 0)
311                         return total;
312
313                 total += ret;
314         }
315         return (ssize_t)total;
316 }
317
318 /****************************************************************************
319  An allocate file space call using the vfs interface.
320  Allocates space for a file from a filedescriptor.
321  Returns 0 on success, -1 on failure.
322 ****************************************************************************/
323
324 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
325 {
326         int ret;
327         SMB_STRUCT_STAT st;
328         connection_struct *conn = fsp->conn;
329         struct vfs_ops *vfs_ops = &conn->vfs_ops;
330         SMB_OFF_T space_avail;
331         SMB_BIG_UINT bsize,dfree,dsize;
332
333         release_level_2_oplocks_on_change(fsp);
334
335         /*
336          * Actually try and commit the space on disk....
337          */
338
339         DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
340
341         ret = vfs_fstat(fsp,fsp->fd,&st);
342         if (ret == -1)
343                 return ret;
344
345         if (len == st.st_size)
346                 return 0;
347
348         if (len < st.st_size) {
349                 /* Shrink - use ftruncate. */
350
351                 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
352                                 fsp->fsp_name, (double)st.st_size ));
353
354                 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
355                         set_filelen_write_cache(fsp, len);
356                 }
357                 return ret;
358         }
359
360         /* Grow - we need to test if we have enough space. */
361
362         if (!lp_strict_allocate(SNUM(fsp->conn)))
363                 return 0;
364
365         len -= st.st_size;
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);
368
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 ));
371
372         if (len > space_avail) {
373                 errno = ENOSPC;
374                 return -1;
375         }
376
377         return 0;
378 }
379
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 ****************************************************************************/
385
386 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
387 {
388         int ret;
389
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         if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
393                 set_filelen_write_cache(fsp, len);
394
395         return ret;
396 }
397
398 /****************************************************************************
399  Transfer some data (n bytes) between two file_struct's.
400 ****************************************************************************/
401
402 static files_struct *in_fsp;
403 static files_struct *out_fsp;
404
405 static ssize_t read_fn(int fd, void *buf, size_t len)
406 {
407         return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
408 }
409
410 static ssize_t write_fn(int fd, const void *buf, size_t len)
411 {
412         return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
413 }
414
415 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
416 {
417         in_fsp = in;
418         out_fsp = out;
419
420         return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
421 }
422
423 /*******************************************************************
424  A vfs_readdir wrapper which just returns the file name.
425 ********************************************************************/
426
427 char *vfs_readdirname(connection_struct *conn, void *p)
428 {
429         struct dirent *ptr;
430         char *dname;
431
432         if (!p)
433                 return(NULL);
434
435         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
436         if (!ptr)
437                 return(NULL);
438
439         dname = ptr->d_name;
440
441 #ifdef NEXT2
442         if (telldir(p) < 0)
443                 return(NULL);
444 #endif
445
446 #ifdef HAVE_BROKEN_READDIR
447         /* using /usr/ucb/cc is BAD */
448         dname = dname - 2;
449 #endif
450
451         return(dname);
452 }
453
454 /* VFS options not quite working yet */
455
456 #if 0
457
458 /***************************************************************************
459   handle the interpretation of the vfs option parameter
460  *************************************************************************/
461 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
462 {
463     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
464     int i;
465
466     /* Create new vfs option */
467
468     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
469     if (new_option == NULL) {
470         return False;
471     }
472
473     ZERO_STRUCTP(new_option);
474
475     /* Get name and value */
476
477     new_option->name = strtok(pszParmValue, "=");
478
479     if (new_option->name == NULL) {
480         return False;
481     }
482
483     while(isspace(*new_option->name)) {
484         new_option->name++;
485     }
486
487     for (i = strlen(new_option->name); i > 0; i--) {
488         if (!isspace(new_option->name[i - 1])) break;
489     }
490
491     new_option->name[i] = '\0';
492     new_option->name = strdup(new_option->name);
493
494     new_option->value = strtok(NULL, "=");
495
496     if (new_option->value != NULL) {
497
498         while(isspace(*new_option->value)) {
499             new_option->value++;
500         }
501         
502         for (i = strlen(new_option->value); i > 0; i--) {
503             if (!isspace(new_option->value[i - 1])) break;
504         }
505         
506         new_option->value[i] = '\0';
507         new_option->value = strdup(new_option->value);
508     }
509
510     /* Add to list */
511
512     DLIST_ADD(*options, new_option);
513
514     return True;
515 }
516
517 #endif
518
519
520 /*******************************************************************
521  A wrapper for vfs_chdir().
522 ********************************************************************/
523
524 int vfs_ChDir(connection_struct *conn, char *path)
525 {
526         int res;
527         static pstring LastDir="";
528
529         if (strcsequal(path,"."))
530                 return(0);
531
532         if (*path == '/' && strcsequal(LastDir,path))
533                 return(0);
534
535         DEBUG(3,("vfs_ChDir to %s\n",path));
536
537         res = vfs_chdir(conn,path);
538         if (!res)
539                 pstrcpy(LastDir,path);
540         return(res);
541 }
542
543 /* number of list structures for a caching GetWd function. */
544 #define MAX_GETWDCACHE (50)
545
546 struct
547 {
548   SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
549   SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
550   char *dos_path; /* The pathname in DOS format. */
551   BOOL valid;
552 } ino_list[MAX_GETWDCACHE];
553
554 extern BOOL use_getwd_cache;
555
556 /****************************************************************************
557  Prompte a ptr (to make it recently used)
558 ****************************************************************************/
559
560 static void array_promote(char *array,int elsize,int element)
561 {
562         char *p;
563         if (element == 0)
564                 return;
565
566         p = (char *)malloc(elsize);
567
568         if (!p) {
569                 DEBUG(5,("array_promote: malloc fail\n"));
570                 return;
571         }
572
573         memcpy(p,array + element * elsize, elsize);
574         memmove(array + elsize,array,elsize*element);
575         memcpy(array,p,elsize);
576         SAFE_FREE(p);
577 }
578
579 /*******************************************************************
580  Return the absolute current directory path - given a UNIX pathname.
581  Note that this path is returned in DOS format, not UNIX
582  format. Note this can be called with conn == NULL.
583 ********************************************************************/
584
585 char *vfs_GetWd(connection_struct *conn, char *path)
586 {
587   pstring s;
588   static BOOL getwd_cache_init = False;
589   SMB_STRUCT_STAT st, st2;
590   int i;
591
592   *s = 0;
593
594   if (!use_getwd_cache)
595     return(vfs_getwd(conn,path));
596
597   /* init the cache */
598   if (!getwd_cache_init)
599   {
600     getwd_cache_init = True;
601     for (i=0;i<MAX_GETWDCACHE;i++)
602     {
603       string_set(&ino_list[i].dos_path,"");
604       ino_list[i].valid = False;
605     }
606   }
607
608   /*  Get the inode of the current directory, if this doesn't work we're
609       in trouble :-) */
610
611   if (vfs_stat(conn, ".",&st) == -1)
612   {
613     DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
614     return(vfs_getwd(conn,path));
615   }
616
617
618   for (i=0; i<MAX_GETWDCACHE; i++)
619     if (ino_list[i].valid)
620     {
621
622       /*  If we have found an entry with a matching inode and dev number
623           then find the inode number for the directory in the cached string.
624           If this agrees with that returned by the stat for the current
625           directory then all is o.k. (but make sure it is a directory all
626           the same...) */
627
628       if (st.st_ino == ino_list[i].inode &&
629           st.st_dev == ino_list[i].dev)
630       {
631         if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0)
632         {
633           if (st.st_ino == st2.st_ino &&
634               st.st_dev == st2.st_dev &&
635               (st2.st_mode & S_IFMT) == S_IFDIR)
636           {
637             pstrcpy (path, ino_list[i].dos_path);
638
639             /* promote it for future use */
640             array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
641             return (path);
642           }
643           else
644           {
645             /*  If the inode is different then something's changed,
646                 scrub the entry and start from scratch. */
647             ino_list[i].valid = False;
648           }
649         }
650       }
651     }
652
653
654   /*  We don't have the information to hand so rely on traditional methods.
655       The very slow getcwd, which spawns a process on some systems, or the
656       not quite so bad getwd. */
657
658   if (!vfs_getwd(conn,s))
659   {
660     DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
661     return (NULL);
662   }
663
664   pstrcpy(path,s);
665
666   DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
667
668   /* add it to the cache */
669   i = MAX_GETWDCACHE - 1;
670   string_set(&ino_list[i].dos_path,s);
671   ino_list[i].dev = st.st_dev;
672   ino_list[i].inode = st.st_ino;
673   ino_list[i].valid = True;
674
675   /* put it at the top of the list */
676   array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
677
678   return (path);
679 }
680
681 /*******************************************************************
682  Reduce a file name, removing .. elements and checking that
683  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
684  on the system that has the referenced file system.
685  Widelinks are allowed if widelinks is true.
686 ********************************************************************/
687
688 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
689 {
690 #ifndef REDUCE_PATHS
691   return True;
692 #else
693   pstring dir2;
694   pstring wd;
695   pstring base_name;
696   pstring newname;
697   char *p=NULL;
698   BOOL relative = (*s != '/');
699
700   *dir2 = *wd = *base_name = *newname = 0;
701
702   if (widelinks)
703   {
704     unix_clean_name(s);
705     /* can't have a leading .. */
706     if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/'))
707     {
708       DEBUG(3,("Illegal file name? (%s)\n",s));
709       return(False);
710     }
711
712     if (strlen(s) == 0)
713       pstrcpy(s,"./");
714
715     return(True);
716   }
717
718   DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
719
720   /* remove any double slashes */
721   all_string_sub(s,"//","/",0);
722
723   pstrcpy(base_name,s);
724   p = strrchr_m(base_name,'/');
725
726   if (!p)
727     return(True);
728
729   if (!vfs_GetWd(conn,wd))
730   {
731     DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
732     return(False);
733   }
734
735   if (vfs_ChDir(conn,dir) != 0)
736   {
737     DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
738     return(False);
739   }
740
741   if (!vfs_GetWd(conn,dir2))
742   {
743     DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
744     vfs_ChDir(conn,wd);
745     return(False);
746   }
747
748   if (p && (p != base_name))
749   {
750     *p = 0;
751     if (strcmp(p+1,".")==0)
752       p[1]=0;
753     if (strcmp(p+1,"..")==0)
754       *p = '/';
755   }
756
757   if (vfs_ChDir(conn,base_name) != 0)
758   {
759     vfs_ChDir(conn,wd);
760     DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
761     return(False);
762   }
763
764   if (!vfs_GetWd(conn,newname))
765   {
766     vfs_ChDir(conn,wd);
767     DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
768     return(False);
769   }
770
771   if (p && (p != base_name))
772   {
773     pstrcat(newname,"/");
774     pstrcat(newname,p+1);
775   }
776
777   {
778     size_t l = strlen(dir2);
779     if (dir2[l-1] == '/')
780       l--;
781
782     if (strncmp(newname,dir2,l) != 0)
783     {
784       vfs_ChDir(conn,wd);
785       DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
786       return(False);
787     }
788
789     if (relative)
790     {
791       if (newname[l] == '/')
792         pstrcpy(s,newname + l + 1);
793       else
794         pstrcpy(s,newname+l);
795     }
796     else
797       pstrcpy(s,newname);
798   }
799
800   vfs_ChDir(conn,wd);
801
802   if (strlen(s) == 0)
803     pstrcpy(s,"./");
804
805   DEBUG(3,("reduced to %s\n",s));
806   return(True);
807 #endif
808 }