Allow the new modules system to function with builtin vfs modules.
[ira/wip.git] / source3 / smbd / vfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    VFS initialisation and support functions
5    Copyright (C) Tim Potter 1999
6    Copyright (C) Alexander Bokovoy 2002
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22    This work was sponsored by Optifacio Software Services, Inc.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_VFS
29
30 struct vfs_init_function_entry {
31    char *name;
32    vfs_op_tuple *ops, *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *);
33    struct vfs_init_function_entry *prev, *next;
34 };
35
36 static struct vfs_init_function_entry *backends = NULL;
37
38 /* Some structures to help us initialise the vfs operations table */
39
40 struct vfs_syminfo {
41         char *name;
42         void *fptr;
43 };
44
45 /*
46   Opaque (final) vfs operations. This is a combination of first-met opaque vfs operations
47   across all currently processed modules.  */
48
49 static vfs_op_tuple vfs_opaque_ops[SMB_VFS_OP_LAST];
50
51 /* Default vfs hooks.  WARNING: The order of these initialisers is
52    very important.  They must be in the same order as defined in
53    vfs.h.  Change at your own peril. */
54
55 static struct vfs_ops default_vfs_ops = {
56
57         /* Disk operations */
58
59         vfswrap_dummy_connect,
60         vfswrap_dummy_disconnect,
61         vfswrap_disk_free,
62
63         /* Directory operations */
64
65         vfswrap_opendir,
66         vfswrap_readdir,
67         vfswrap_mkdir,
68         vfswrap_rmdir,
69         vfswrap_closedir,
70
71         /* File operations */
72
73         vfswrap_open,
74         vfswrap_close,
75         vfswrap_read,
76         vfswrap_write,
77         vfswrap_lseek,
78         vfswrap_sendfile,
79         vfswrap_rename,
80         vfswrap_fsync,
81         vfswrap_stat,
82         vfswrap_fstat,
83         vfswrap_lstat,
84         vfswrap_unlink,
85         vfswrap_chmod,
86         vfswrap_fchmod,
87         vfswrap_chown,
88         vfswrap_fchown,
89         vfswrap_chdir,
90         vfswrap_getwd,
91         vfswrap_utime,
92         vfswrap_ftruncate,
93         vfswrap_lock,
94         vfswrap_symlink,
95         vfswrap_readlink,
96         vfswrap_link,
97         vfswrap_mknod,
98         vfswrap_realpath,
99
100         vfswrap_fget_nt_acl,
101         vfswrap_get_nt_acl,
102         vfswrap_fset_nt_acl,
103         vfswrap_set_nt_acl,
104
105         /* POSIX ACL operations. */
106 #if defined(HAVE_NO_ACLS)
107         NULL,
108         NULL,
109 #else
110         vfswrap_chmod_acl,
111         vfswrap_fchmod_acl,
112 #endif
113         vfswrap_sys_acl_get_entry,
114         vfswrap_sys_acl_get_tag_type,
115         vfswrap_sys_acl_get_permset,
116         vfswrap_sys_acl_get_qualifier,
117         vfswrap_sys_acl_get_file,
118         vfswrap_sys_acl_get_fd,
119         vfswrap_sys_acl_clear_perms,
120         vfswrap_sys_acl_add_perm,
121         vfswrap_sys_acl_to_text,
122         vfswrap_sys_acl_init,
123         vfswrap_sys_acl_create_entry,
124         vfswrap_sys_acl_set_tag_type,
125         vfswrap_sys_acl_set_qualifier,
126         vfswrap_sys_acl_set_permset,
127         vfswrap_sys_acl_valid,
128         vfswrap_sys_acl_set_file,
129         vfswrap_sys_acl_set_fd,
130         vfswrap_sys_acl_delete_def_file,
131         vfswrap_sys_acl_get_perm,
132         vfswrap_sys_acl_free_text,
133         vfswrap_sys_acl_free_acl,
134         vfswrap_sys_acl_free_qualifier
135 };
136
137 /****************************************************************************
138     maintain the list of available backends
139 ****************************************************************************/
140
141 struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
142 {
143    struct vfs_init_function_entry *entry = backends;
144
145    while(entry) {
146        if (strequal(entry->name, name)) return entry;
147        entry = entry->next;
148    }
149
150    return NULL;
151 }
152
153 BOOL smb_register_vfs(const char *name, vfs_op_tuple *(*init)(const struct vfs_ops *, struct smb_vfs_handle_struct *), int version)
154 {
155    struct vfs_init_function_entry *entry = backends;
156    
157    if ((version < SMB_VFS_INTERFACE_CASCADED)) {
158        DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
159            version, SMB_VFS_INTERFACE_VERSION ));
160        return False;
161    }
162   
163    if ((version < SMB_VFS_INTERFACE_VERSION)) {
164        DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
165                 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
166                 version, SMB_VFS_INTERFACE_VERSION, version ));
167            return False;
168    }
169    
170    while(entry) {
171        if (strequal(entry->name, name)) {
172            DEBUG(0,("VFS module %s already loaded!\n", name));
173            return False;
174        }
175        entry = entry->next;
176    }
177
178    entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
179    entry->name = smb_xstrdup(name);
180    entry->init = init;
181
182    DLIST_ADD(backends, entry);
183    DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
184    return True;
185 }
186
187 /****************************************************************************
188   initialise default vfs hooks
189 ****************************************************************************/
190
191 static void vfs_init_default(connection_struct *conn)
192 {
193         DEBUG(3, ("Initialising default vfs hooks\n"));
194
195         memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops));
196         conn->vfs_private = NULL;
197 }
198
199 /***************************************************************************
200  Function to load old VFS modules. Should go away after a while.
201  **************************************************************************/
202
203 static BOOL vfs_load_old_plugin(connection_struct *conn, const char *vfs_object)
204 {
205         int vfs_version = -1;
206         vfs_op_tuple *ops, *(*init_fptr)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *);
207         /* Open object file */
208
209         if ((conn->vfs_private->handle = sys_dlopen(vfs_object, RTLD_NOW)) == NULL) {
210                 DEBUG(0, ("Error opening %s: %s\n", vfs_object, sys_dlerror()));
211                 return False;
212         }
213
214         /* Get handle on vfs_init() symbol */
215
216         init_fptr = (vfs_op_tuple *(*)(int *, const struct vfs_ops *, struct smb_vfs_handle_struct *))sys_dlsym(conn->vfs_private->handle, "vfs_init");
217
218         if (init_fptr == NULL) {
219                 DEBUG(0, ("No vfs_init() symbol found in %s\n", vfs_object));
220                 sys_dlclose(conn->vfs_private->handle);
221                 return False;
222         }
223
224         /* Initialise vfs_ops structure */
225         if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
226                 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
227                 sys_dlclose(conn->vfs_private->handle);
228                 return False;
229         }
230
231         if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
232                 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
233                                   vfs_version, SMB_VFS_INTERFACE_VERSION ));
234                 sys_dlclose(conn->vfs_private->handle);
235                 return False;
236         }
237
238         if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
239                 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
240                                   Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
241                                   vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
242                 sys_dlclose(conn->vfs_private->handle);
243                 return False;
244         }
245         
246         return True;
247 }
248
249
250
251 /****************************************************************************
252   initialise custom vfs hooks
253  ****************************************************************************/
254
255 BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
256 {
257         vfs_op_tuple *ops;
258         int i;
259         struct vfs_init_function_entry *entry;
260
261         DEBUG(3, ("Initialising custom vfs hooks from %s\n", vfs_object));
262
263         if(!backends) static_init_vfs;
264
265         /* First, try to load the module with the new module system */
266         if((entry = vfs_find_backend_entry(vfs_object)) || 
267            (smb_probe_module("vfs", vfs_object) && 
268                 (entry = vfs_find_backend_entry(vfs_object)))) {
269
270                 DEBUG(0,("Successfully loaded %s with the new modules system\n", vfs_object));
271                 
272                 if ((ops = entry->init(&conn->vfs_ops, conn->vfs_private)) == NULL) {
273                         DEBUG(0, ("vfs init function from %s failed\n", vfs_object));
274                         return False;
275                 }
276         } else {
277                 /* If that doesn't work, fall back to the old system 
278                  * (This part should go away after a while, it's only here 
279                  * for backwards compatibility) */
280                 DEBUG(2, ("Can't load module with new modules system, falling back to old\n"));
281                 if (!vfs_load_old_plugin(conn, vfs_object)) return False;
282         }
283   
284         for(i=0; ops[i].op != NULL; i++) {
285           DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
286           if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
287             /* Check whether this operation was already made opaque by different module */
288             if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
289               /* No, it isn't overloaded yet. Overload. */
290               DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
291               vfs_opaque_ops[ops[i].type] = ops[i];
292             }
293           }
294           /* Change current VFS disposition*/
295           DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
296           ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
297         }
298
299         return True;
300 }
301
302 /*****************************************************************
303  Generic VFS init.
304 ******************************************************************/
305
306 BOOL smbd_vfs_init(connection_struct *conn)
307 {
308         const char **vfs_objects;
309         char *vfs_module, *vfs_path;
310         unsigned int i;
311         unsigned int j = 0;
312         struct smb_vfs_handle_struct *handle;
313         
314         /* Normal share - initialise with disk access functions */
315         vfs_init_default(conn);
316         vfs_objects = lp_vfsobj(SNUM(conn));
317
318         /* Override VFS functions if 'vfs object' was specified*/
319         if (!vfs_objects)
320                 return True;
321
322         for(i=0; i<SMB_VFS_OP_LAST; i++) {
323                 vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
324                 vfs_opaque_ops[i].type = i;
325                 vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
326         }
327
328         vfs_path = lp_vfs_path(SNUM(conn));
329         
330         for (j=0; vfs_objects[j]; j++) {
331                 conn->vfs_private = NULL;
332                 handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
333                 /* Loadable object file */
334                 handle->handle = NULL;
335                 DLIST_ADD(conn->vfs_private, handle);
336                 vfs_module = NULL;
337                 if (vfs_path && *vfs_path) {
338                         asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[j]);
339                 } else {
340                         asprintf(&vfs_module, "%s", vfs_objects[j]);
341                 }
342                 if (!vfs_init_custom(conn, vfs_module)) {
343                         DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
344                         SAFE_FREE(vfs_module);
345                         DLIST_REMOVE(conn->vfs_private, handle);
346                         SAFE_FREE(handle);
347                         return False;
348                 }
349                 SAFE_FREE(vfs_module);
350         }
351         return True;
352 }
353
354 /*******************************************************************
355  Create vfs_ops reflecting current vfs_opaque_ops
356 *******************************************************************/
357
358 struct vfs_ops *smb_vfs_get_opaque_ops(void)
359 {
360   int i;
361   struct vfs_ops *ops;
362
363   ops = smb_xmalloc(sizeof(struct vfs_ops));
364
365   for(i=0; i<SMB_VFS_OP_LAST; i++) {
366     ((void**)ops)[i] = vfs_opaque_ops[i].op;
367   }
368   return ops;
369 }
370
371 /*******************************************************************
372  Check if directory exists.
373 ********************************************************************/
374
375 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
376 {
377         SMB_STRUCT_STAT st2;
378         BOOL ret;
379
380         if (!st)
381                 st = &st2;
382
383         if (vfs_stat(conn,dname,st) != 0)
384                 return(False);
385
386         ret = S_ISDIR(st->st_mode);
387         if(!ret)
388                 errno = ENOTDIR;
389
390         return ret;
391 }
392
393 /*******************************************************************
394  vfs getwd wrapper 
395 ********************************************************************/
396
397 static char *vfs_getwd(connection_struct *conn, char *path)
398 {
399         return conn->vfs_ops.getwd(conn,path);
400 }
401
402 /*******************************************************************
403  vfs mkdir wrapper 
404 ********************************************************************/
405
406 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
407 {
408         int ret;
409         SMB_STRUCT_STAT sbuf;
410
411         if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
412
413                 inherit_access_acl(conn, name, mode);
414
415                 /*
416                  * Check if high bits should have been set,
417                  * then (if bits are missing): add them.
418                  * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
419                  */
420                 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
421                                 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
422                         vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
423         }
424         return ret;
425 }
426
427 /*******************************************************************
428  Check if an object exists in the vfs.
429 ********************************************************************/
430
431 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
432 {
433         SMB_STRUCT_STAT st;
434
435         if (!sbuf)
436                 sbuf = &st;
437
438         ZERO_STRUCTP(sbuf);
439
440         if (vfs_stat(conn,fname,sbuf) == -1)
441                 return(False);
442         return True;
443 }
444
445 /*******************************************************************
446  Check if a file exists in the vfs.
447 ********************************************************************/
448
449 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
450 {
451         SMB_STRUCT_STAT st;
452
453         if (!sbuf)
454                 sbuf = &st;
455
456         ZERO_STRUCTP(sbuf);
457
458         if (vfs_stat(conn,fname,sbuf) == -1)
459                 return False;
460         return(S_ISREG(sbuf->st_mode));
461 }
462
463 /****************************************************************************
464  Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
465 ****************************************************************************/
466
467 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
468 {
469         size_t total=0;
470
471         while (total < byte_count)
472         {
473                 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
474                                         byte_count - total);
475
476                 if (ret == 0) return total;
477                 if (ret == -1) {
478                         if (errno == EINTR)
479                                 continue;
480                         else
481                                 return -1;
482                 }
483                 total += ret;
484         }
485         return (ssize_t)total;
486 }
487
488 /****************************************************************************
489  Write data to a fd on the vfs.
490 ****************************************************************************/
491
492 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
493 {
494         size_t total=0;
495         ssize_t ret;
496
497         while (total < N) {
498                 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
499
500                 if (ret == -1)
501                         return -1;
502                 if (ret == 0)
503                         return total;
504
505                 total += ret;
506         }
507         return (ssize_t)total;
508 }
509
510 /****************************************************************************
511  An allocate file space call using the vfs interface.
512  Allocates space for a file from a filedescriptor.
513  Returns 0 on success, -1 on failure.
514 ****************************************************************************/
515
516 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
517 {
518         int ret;
519         SMB_STRUCT_STAT st;
520         connection_struct *conn = fsp->conn;
521         struct vfs_ops *vfs_ops = &conn->vfs_ops;
522         SMB_BIG_UINT space_avail;
523         SMB_BIG_UINT bsize,dfree,dsize;
524
525         release_level_2_oplocks_on_change(fsp);
526
527         /*
528          * Actually try and commit the space on disk....
529          */
530
531         DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
532
533         if (((SMB_OFF_T)len) < 0) {
534                 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
535                 return -1;
536         }
537
538         ret = vfs_fstat(fsp,fsp->fd,&st);
539         if (ret == -1)
540                 return ret;
541
542         if (len == (SMB_BIG_UINT)st.st_size)
543                 return 0;
544
545         if (len < (SMB_BIG_UINT)st.st_size) {
546                 /* Shrink - use ftruncate. */
547
548                 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
549                                 fsp->fsp_name, (double)st.st_size ));
550
551                 flush_write_cache(fsp, SIZECHANGE_FLUSH);
552                 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
553                         set_filelen_write_cache(fsp, len);
554                 }
555                 return ret;
556         }
557
558         /* Grow - we need to test if we have enough space. */
559
560         if (!lp_strict_allocate(SNUM(fsp->conn)))
561                 return 0;
562
563         len -= st.st_size;
564         len /= 1024; /* Len is now number of 1k blocks needed. */
565         space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
566
567         DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
568                         fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
569
570         if (len > space_avail) {
571                 errno = ENOSPC;
572                 return -1;
573         }
574
575         return 0;
576 }
577
578 /****************************************************************************
579  A vfs set_filelen call.
580  set the length of a file from a filedescriptor.
581  Returns 0 on success, -1 on failure.
582 ****************************************************************************/
583
584 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
585 {
586         int ret;
587
588         release_level_2_oplocks_on_change(fsp);
589         DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
590         flush_write_cache(fsp, SIZECHANGE_FLUSH);
591         if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
592                 set_filelen_write_cache(fsp, len);
593
594         return ret;
595 }
596
597 /****************************************************************************
598  Transfer some data (n bytes) between two file_struct's.
599 ****************************************************************************/
600
601 static files_struct *in_fsp;
602 static files_struct *out_fsp;
603
604 static ssize_t read_fn(int fd, void *buf, size_t len)
605 {
606         return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
607 }
608
609 static ssize_t write_fn(int fd, const void *buf, size_t len)
610 {
611         return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
612 }
613
614 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
615 {
616         in_fsp = in;
617         out_fsp = out;
618
619         return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
620 }
621
622 /*******************************************************************
623  A vfs_readdir wrapper which just returns the file name.
624 ********************************************************************/
625
626 char *vfs_readdirname(connection_struct *conn, void *p)
627 {
628         struct dirent *ptr;
629         char *dname;
630
631         if (!p)
632                 return(NULL);
633
634         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
635         if (!ptr)
636                 return(NULL);
637
638         dname = ptr->d_name;
639
640 #ifdef NEXT2
641         if (telldir(p) < 0)
642                 return(NULL);
643 #endif
644
645 #ifdef HAVE_BROKEN_READDIR
646         /* using /usr/ucb/cc is BAD */
647         dname = dname - 2;
648 #endif
649
650         return(dname);
651 }
652
653 /* VFS options not quite working yet */
654
655 #if 0
656
657 /***************************************************************************
658   handle the interpretation of the vfs option parameter
659  *************************************************************************/
660 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
661 {
662     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
663     int i;
664
665     /* Create new vfs option */
666
667     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
668     if (new_option == NULL) {
669         return False;
670     }
671
672     ZERO_STRUCTP(new_option);
673
674     /* Get name and value */
675
676     new_option->name = strtok(pszParmValue, "=");
677
678     if (new_option->name == NULL) {
679         return False;
680     }
681
682     while(isspace(*new_option->name)) {
683         new_option->name++;
684     }
685
686     for (i = strlen(new_option->name); i > 0; i--) {
687         if (!isspace(new_option->name[i - 1])) break;
688     }
689
690     new_option->name[i] = '\0';
691     new_option->name = strdup(new_option->name);
692
693     new_option->value = strtok(NULL, "=");
694
695     if (new_option->value != NULL) {
696
697         while(isspace(*new_option->value)) {
698             new_option->value++;
699         }
700         
701         for (i = strlen(new_option->value); i > 0; i--) {
702             if (!isspace(new_option->value[i - 1])) break;
703         }
704         
705         new_option->value[i] = '\0';
706         new_option->value = strdup(new_option->value);
707     }
708
709     /* Add to list */
710
711     DLIST_ADD(*options, new_option);
712
713     return True;
714 }
715
716 #endif
717
718
719 /*******************************************************************
720  A wrapper for vfs_chdir().
721 ********************************************************************/
722
723 int vfs_ChDir(connection_struct *conn, const char *path)
724 {
725         int res;
726         static pstring LastDir="";
727
728         if (strcsequal(path,"."))
729                 return(0);
730
731         if (*path == '/' && strcsequal(LastDir,path))
732                 return(0);
733
734         DEBUG(3,("vfs_ChDir to %s\n",path));
735
736         res = vfs_chdir(conn,path);
737         if (!res)
738                 pstrcpy(LastDir,path);
739         return(res);
740 }
741
742 /* number of list structures for a caching GetWd function. */
743 #define MAX_GETWDCACHE (50)
744
745 static struct {
746         SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
747         SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
748         char *dos_path; /* The pathname in DOS format. */
749         BOOL valid;
750 } ino_list[MAX_GETWDCACHE];
751
752 extern BOOL use_getwd_cache;
753
754 /****************************************************************************
755  Prompte a ptr (to make it recently used)
756 ****************************************************************************/
757
758 static void array_promote(char *array,int elsize,int element)
759 {
760         char *p;
761         if (element == 0)
762                 return;
763
764         p = (char *)malloc(elsize);
765
766         if (!p) {
767                 DEBUG(5,("array_promote: malloc fail\n"));
768                 return;
769         }
770
771         memcpy(p,array + element * elsize, elsize);
772         memmove(array + elsize,array,elsize*element);
773         memcpy(array,p,elsize);
774         SAFE_FREE(p);
775 }
776
777 /*******************************************************************
778  Return the absolute current directory path - given a UNIX pathname.
779  Note that this path is returned in DOS format, not UNIX
780  format. Note this can be called with conn == NULL.
781 ********************************************************************/
782
783 char *vfs_GetWd(connection_struct *conn, char *path)
784 {
785         pstring s;
786         static BOOL getwd_cache_init = False;
787         SMB_STRUCT_STAT st, st2;
788         int i;
789
790         *s = 0;
791
792         if (!use_getwd_cache)
793                 return(vfs_getwd(conn,path));
794
795         /* init the cache */
796         if (!getwd_cache_init) {
797                 getwd_cache_init = True;
798                 for (i=0;i<MAX_GETWDCACHE;i++) {
799                         string_set(&ino_list[i].dos_path,"");
800                         ino_list[i].valid = False;
801                 }
802         }
803
804         /*  Get the inode of the current directory, if this doesn't work we're
805                 in trouble :-) */
806
807         if (vfs_stat(conn, ".",&st) == -1) {
808                 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
809                 return(vfs_getwd(conn,path));
810         }
811
812
813         for (i=0; i<MAX_GETWDCACHE; i++) {
814                 if (ino_list[i].valid) {
815
816                         /*  If we have found an entry with a matching inode and dev number
817                                 then find the inode number for the directory in the cached string.
818                                 If this agrees with that returned by the stat for the current
819                                 directory then all is o.k. (but make sure it is a directory all
820                                 the same...) */
821
822                         if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
823                                 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
824                                         if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
825                                                         (st2.st_mode & S_IFMT) == S_IFDIR) {
826                                                 pstrcpy (path, ino_list[i].dos_path);
827
828                                                 /* promote it for future use */
829                                                 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
830                                                 return (path);
831                                         } else {
832                                                 /*  If the inode is different then something's changed,
833                                                         scrub the entry and start from scratch. */
834                                                 ino_list[i].valid = False;
835                                         }
836                                 }
837                         }
838                 }
839         }
840
841         /*  We don't have the information to hand so rely on traditional methods.
842                 The very slow getcwd, which spawns a process on some systems, or the
843                 not quite so bad getwd. */
844
845         if (!vfs_getwd(conn,s)) {
846                 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
847                 return (NULL);
848         }
849
850         pstrcpy(path,s);
851
852         DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
853
854         /* add it to the cache */
855         i = MAX_GETWDCACHE - 1;
856         string_set(&ino_list[i].dos_path,s);
857         ino_list[i].dev = st.st_dev;
858         ino_list[i].inode = st.st_ino;
859         ino_list[i].valid = True;
860
861         /* put it at the top of the list */
862         array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
863
864         return (path);
865 }
866
867
868 /* check if the file 'nmae' is a symlink, in that case check that it point to
869    a file that reside under the 'dir' tree */
870
871 static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
872 {
873         BOOL ret = True;
874         pstring flink;
875         pstring cleanlink;
876         pstring savedir;
877         pstring realdir;
878         size_t reallen;
879
880         if (!vfs_GetWd(conn, savedir)) {
881                 DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
882                 return False;
883         }
884
885         if (vfs_ChDir(conn, dir) != 0) {
886                 DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
887                 return False;
888         }
889
890         if (!vfs_GetWd(conn, realdir)) {
891                 DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
892                 vfs_ChDir(conn, savedir);
893                 return(False);
894         }
895         
896         reallen = strlen(realdir);
897         if (realdir[reallen -1] == '/') {
898                 reallen--;
899                 realdir[reallen] = 0;
900         }
901
902         if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
903                 DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
904                 if (*flink == '/') {
905                         pstrcpy(cleanlink, flink);
906                 } else {
907                         pstrcpy(cleanlink, realdir);
908                         pstrcat(cleanlink, "/");
909                         pstrcat(cleanlink, flink);
910                 }
911                 unix_clean_name(cleanlink);
912
913                 if (strncmp(cleanlink, realdir, reallen) != 0) {
914                         DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
915                         ret = False;
916                 }
917         }
918
919         vfs_ChDir(conn, savedir);
920         
921         return ret;
922 }
923
924 /*******************************************************************
925  Reduce a file name, removing .. elements and checking that
926  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
927  on the system that has the referenced file system.
928  Widelinks are allowed if widelinks is true.
929 ********************************************************************/
930
931 BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL widelinks)
932 {
933 #ifndef REDUCE_PATHS
934         return True;
935 #else
936         pstring dir2;
937         pstring wd;
938         pstring base_name;
939         pstring newname;
940         char *p=NULL;
941         BOOL relative = (*s != '/');
942
943         *dir2 = *wd = *base_name = *newname = 0;
944
945         if (widelinks) {
946                 unix_clean_name(s);
947                 /* can't have a leading .. */
948                 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
949                         DEBUG(3,("Illegal file name? (%s)\n",s));
950                         return(False);
951                 }
952
953                 if (strlen(s) == 0)
954                         pstrcpy(s,"./");
955
956                 return(True);
957         }
958
959         DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
960
961         /* remove any double slashes */
962         all_string_sub(s,"//","/",0);
963
964         pstrcpy(base_name,s);
965         p = strrchr_m(base_name,'/');
966
967         if (!p)
968                 return readlink_check(conn, dir, s);
969
970         if (!vfs_GetWd(conn,wd)) {
971                 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
972                 return(False);
973         }
974
975         if (vfs_ChDir(conn,dir) != 0) {
976                 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
977                 return(False);
978         }
979
980         if (!vfs_GetWd(conn,dir2)) {
981                 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
982                 vfs_ChDir(conn,wd);
983                 return(False);
984         }
985
986         if (p && (p != base_name)) {
987                 *p = 0;
988                 if (strcmp(p+1,".")==0)
989                         p[1]=0;
990                 if (strcmp(p+1,"..")==0)
991                         *p = '/';
992         }
993
994         if (vfs_ChDir(conn,base_name) != 0) {
995                 vfs_ChDir(conn,wd);
996                 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
997                 return(False);
998         }
999
1000         if (!vfs_GetWd(conn,newname)) {
1001                 vfs_ChDir(conn,wd);
1002                 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
1003                 return(False);
1004         }
1005
1006         if (p && (p != base_name)) {
1007                 pstrcat(newname,"/");
1008                 pstrcat(newname,p+1);
1009         }
1010
1011         {
1012                 size_t l = strlen(dir2);
1013                 if (dir2[l-1] == '/')
1014                         l--;
1015
1016                 if (strncmp(newname,dir2,l) != 0) {
1017                         vfs_ChDir(conn,wd);
1018                         DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
1019                         return(False);
1020                 }
1021
1022                 if (!readlink_check(conn, dir, newname)) {
1023                         DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
1024                         return(False);
1025                 }
1026
1027                 if (relative) {
1028                         if (newname[l] == '/')
1029                                 pstrcpy(s,newname + l + 1);
1030                         else
1031                                 pstrcpy(s,newname+l);
1032                 } else
1033                         pstrcpy(s,newname);
1034         }
1035
1036         vfs_ChDir(conn,wd);
1037
1038         if (strlen(s) == 0)
1039                 pstrcpy(s,"./");
1040
1041         DEBUG(3,("reduced to %s\n",s));
1042         return(True);
1043 #endif
1044 }
1045