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