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