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