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