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