Fix for 64 bit issues with oplocks and allocation size.
[samba.git] / source3 / smbd / vfs.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    VFS initialisation and support functions
5    Copyright (C) Tim Potter 1999
6    Copyright (C) Alexander Bokovoy 2002
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22    This work was sponsored by Optifacio Software Services, Inc.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_VFS
29
30
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                 return False;
168         }
169
170         /* Initialise vfs_ops structure */
171
172         if ((ops = init_fptr(&vfs_version, &conn->vfs_ops, conn->vfs_private)) == NULL) {
173                 DEBUG(0, ("vfs_init() function from %s failed\n", vfs_object));
174                 return False;
175         }
176   
177         if ((vfs_version < SMB_VFS_INTERFACE_CASCADED)) {
178                 DEBUG(0, ("vfs_init() returned wrong interface version info (was %d, should be no less than %d)\n",
179                         vfs_version, SMB_VFS_INTERFACE_VERSION ));
180                 return False;
181         }
182   
183         if ((vfs_version < SMB_VFS_INTERFACE_VERSION)) {
184                 DEBUG(0, ("Warning: vfs_init() states that module confirms interface version #%d, current interface version is #%d.\n\
185 Proceeding in compatibility mode, new operations (since version #%d) will fallback to default ones.\n",
186                         vfs_version, SMB_VFS_INTERFACE_VERSION, vfs_version ));
187                 return False;
188         }
189   
190         for(i=0; ops[i].op != NULL; i++) {
191           DEBUG(3, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
192           if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
193             /* Check whether this operation was already made opaque by different module */
194             if(vfs_opaque_ops[ops[i].type].op == ((void**)&default_vfs_ops)[ops[i].type]) {
195               /* No, it isn't overloaded yet. Overload. */
196               DEBUG(3, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
197               vfs_opaque_ops[ops[i].type] = ops[i];
198             }
199           }
200           /* Change current VFS disposition*/
201           DEBUG(3, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
202           ((void**)&conn->vfs_ops)[ops[i].type] = ops[i].op;
203         }
204
205         return True;
206 }
207
208 /*****************************************************************
209  Generic VFS init.
210 ******************************************************************/
211
212 BOOL smbd_vfs_init(connection_struct *conn)
213 {
214         char **vfs_objects, *vfsobj, *vfs_module, *vfs_path;
215         int nobj, i;
216         struct smb_vfs_handle_struct *handle;
217         
218         /* Normal share - initialise with disk access functions */
219         vfs_init_default(conn);
220
221         /* Override VFS functions if 'vfs object' was specified*/
222         if (*lp_vfsobj(SNUM(conn))) {
223                 vfsobj = NULL;
224                 for(i=0; i<SMB_VFS_OP_LAST; i++) {
225                   vfs_opaque_ops[i].op = ((void**)&default_vfs_ops)[i];
226                   vfs_opaque_ops[i].type = i;
227                   vfs_opaque_ops[i].layer = SMB_VFS_LAYER_OPAQUE;
228                 }
229                 if (string_set(&vfsobj, lp_vfsobj(SNUM(conn)))) {
230                         /* Parse passed modules specification to array of modules */
231                         set_first_token(vfsobj);
232                         /* We are using default separators: ' \t\r\n' */
233                         vfs_objects = toktocliplist(&nobj, NULL);
234                         if (vfs_objects) {
235                                 vfs_path = lp_vfs_path(SNUM(conn));
236                                 conn->vfs_private = NULL;
237                                 for(i=nobj-1; i>=0; i--) {
238                                         handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
239                                         /* Loadable object file */
240                                         handle->handle = NULL;
241                                         DLIST_ADD(conn->vfs_private, handle)
242                                         vfs_module = NULL;
243                                         if (vfs_path) {
244                                                 asprintf(&vfs_module, "%s/%s", vfs_path, vfs_objects[i]);
245                                         } else {
246                                                 asprintf(&vfs_module, "%s", vfs_objects[i]);
247                                         }
248                                         if (!vfs_init_custom(conn, vfs_module)) {
249                                                 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_module));
250                                                 string_free(&vfsobj);
251                                                 SAFE_FREE(vfs_module);
252                                                 return False;
253                                         }
254                                         SAFE_FREE(vfs_module);
255                                 }
256                         }
257                         string_free(&vfsobj);
258                         return True;
259                 }
260         }
261         return True;
262 }
263
264 /*******************************************************************
265  Create vfs_ops reflecting current vfs_opaque_ops
266 *******************************************************************/
267
268 struct vfs_ops *smb_vfs_get_opaque_ops(void)
269 {
270   int i;
271   struct vfs_ops *ops;
272
273   ops = smb_xmalloc(sizeof(struct vfs_ops));
274
275   for(i=0; i<SMB_VFS_OP_LAST; i++) {
276     ((void**)ops)[i] = vfs_opaque_ops[i].op;
277   }
278   return ops;
279 }
280
281 /*******************************************************************
282  Check if directory exists.
283 ********************************************************************/
284
285 BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
286 {
287         SMB_STRUCT_STAT st2;
288         BOOL ret;
289
290         if (!st)
291                 st = &st2;
292
293         if (vfs_stat(conn,dname,st) != 0)
294                 return(False);
295
296         ret = S_ISDIR(st->st_mode);
297         if(!ret)
298                 errno = ENOTDIR;
299
300         return ret;
301 }
302
303 /*******************************************************************
304  vfs getwd wrapper 
305 ********************************************************************/
306
307 static char *vfs_getwd(connection_struct *conn, char *path)
308 {
309         return conn->vfs_ops.getwd(conn,path);
310 }
311
312 /*******************************************************************
313  vfs mkdir wrapper 
314 ********************************************************************/
315
316 int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
317 {
318         int ret;
319         SMB_STRUCT_STAT sbuf;
320
321         if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) {
322
323                 inherit_access_acl(conn, name, mode);
324
325                 /*
326                  * Check if high bits should have been set,
327                  * then (if bits are missing): add them.
328                  * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
329                  */
330                 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
331                                 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
332                         vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
333         }
334         return ret;
335 }
336
337 /*******************************************************************
338  Check if an object exists in the vfs.
339 ********************************************************************/
340
341 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
342 {
343         SMB_STRUCT_STAT st;
344
345         if (!sbuf)
346                 sbuf = &st;
347
348         ZERO_STRUCTP(sbuf);
349
350         if (vfs_stat(conn,fname,sbuf) == -1)
351                 return(False);
352         return True;
353 }
354
355 /*******************************************************************
356  Check if a file exists in the vfs.
357 ********************************************************************/
358
359 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
360 {
361         SMB_STRUCT_STAT st;
362
363         if (!sbuf)
364                 sbuf = &st;
365
366         ZERO_STRUCTP(sbuf);
367
368         if (vfs_stat(conn,fname,sbuf) == -1)
369                 return False;
370         return(S_ISREG(sbuf->st_mode));
371 }
372
373 /****************************************************************************
374  Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
375 ****************************************************************************/
376
377 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
378 {
379         size_t total=0;
380
381         while (total < byte_count)
382         {
383                 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
384                                         byte_count - total);
385
386                 if (ret == 0) return total;
387                 if (ret == -1) {
388                         if (errno == EINTR)
389                                 continue;
390                         else
391                                 return -1;
392                 }
393                 total += ret;
394         }
395         return (ssize_t)total;
396 }
397
398 /****************************************************************************
399  Write data to a fd on the vfs.
400 ****************************************************************************/
401
402 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
403 {
404         size_t total=0;
405         ssize_t ret;
406
407         while (total < N) {
408                 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
409
410                 if (ret == -1)
411                         return -1;
412                 if (ret == 0)
413                         return total;
414
415                 total += ret;
416         }
417         return (ssize_t)total;
418 }
419
420 /****************************************************************************
421  An allocate file space call using the vfs interface.
422  Allocates space for a file from a filedescriptor.
423  Returns 0 on success, -1 on failure.
424 ****************************************************************************/
425
426 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
427 {
428         int ret;
429         SMB_STRUCT_STAT st;
430         connection_struct *conn = fsp->conn;
431         struct vfs_ops *vfs_ops = &conn->vfs_ops;
432         SMB_BIG_UINT space_avail;
433         SMB_BIG_UINT bsize,dfree,dsize;
434
435         release_level_2_oplocks_on_change(fsp);
436
437         /*
438          * Actually try and commit the space on disk....
439          */
440
441         DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
442
443         if (((SMB_OFF_T)len) < 0) {
444                 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
445                 return -1;
446         }
447
448         ret = vfs_fstat(fsp,fsp->fd,&st);
449         if (ret == -1)
450                 return ret;
451
452         if (len == (SMB_BIG_UINT)st.st_size)
453                 return 0;
454
455         if (len < (SMB_BIG_UINT)st.st_size) {
456                 /* Shrink - use ftruncate. */
457
458                 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
459                                 fsp->fsp_name, (double)st.st_size ));
460
461                 flush_write_cache(fsp, SIZECHANGE_FLUSH);
462                 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
463                         set_filelen_write_cache(fsp, len);
464                 }
465                 return ret;
466         }
467
468         /* Grow - we need to test if we have enough space. */
469
470         if (!lp_strict_allocate(SNUM(fsp->conn)))
471                 return 0;
472
473         len -= st.st_size;
474         len /= 1024; /* Len is now number of 1k blocks needed. */
475         space_avail = conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
476
477         DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
478                         fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
479
480         if (len > space_avail) {
481                 errno = ENOSPC;
482                 return -1;
483         }
484
485         return 0;
486 }
487
488 /****************************************************************************
489  A vfs set_filelen call.
490  set the length of a file from a filedescriptor.
491  Returns 0 on success, -1 on failure.
492 ****************************************************************************/
493
494 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
495 {
496         int ret;
497
498         release_level_2_oplocks_on_change(fsp);
499         DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
500         flush_write_cache(fsp, SIZECHANGE_FLUSH);
501         if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
502                 set_filelen_write_cache(fsp, len);
503
504         return ret;
505 }
506
507 /****************************************************************************
508  Transfer some data (n bytes) between two file_struct's.
509 ****************************************************************************/
510
511 static files_struct *in_fsp;
512 static files_struct *out_fsp;
513
514 static ssize_t read_fn(int fd, void *buf, size_t len)
515 {
516         return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
517 }
518
519 static ssize_t write_fn(int fd, const void *buf, size_t len)
520 {
521         return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
522 }
523
524 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
525 {
526         in_fsp = in;
527         out_fsp = out;
528
529         return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
530 }
531
532 /*******************************************************************
533  A vfs_readdir wrapper which just returns the file name.
534 ********************************************************************/
535
536 char *vfs_readdirname(connection_struct *conn, void *p)
537 {
538         struct dirent *ptr;
539         char *dname;
540
541         if (!p)
542                 return(NULL);
543
544         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
545         if (!ptr)
546                 return(NULL);
547
548         dname = ptr->d_name;
549
550 #ifdef NEXT2
551         if (telldir(p) < 0)
552                 return(NULL);
553 #endif
554
555 #ifdef HAVE_BROKEN_READDIR
556         /* using /usr/ucb/cc is BAD */
557         dname = dname - 2;
558 #endif
559
560         return(dname);
561 }
562
563 /* VFS options not quite working yet */
564
565 #if 0
566
567 /***************************************************************************
568   handle the interpretation of the vfs option parameter
569  *************************************************************************/
570 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
571 {
572     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
573     int i;
574
575     /* Create new vfs option */
576
577     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
578     if (new_option == NULL) {
579         return False;
580     }
581
582     ZERO_STRUCTP(new_option);
583
584     /* Get name and value */
585
586     new_option->name = strtok(pszParmValue, "=");
587
588     if (new_option->name == NULL) {
589         return False;
590     }
591
592     while(isspace(*new_option->name)) {
593         new_option->name++;
594     }
595
596     for (i = strlen(new_option->name); i > 0; i--) {
597         if (!isspace(new_option->name[i - 1])) break;
598     }
599
600     new_option->name[i] = '\0';
601     new_option->name = strdup(new_option->name);
602
603     new_option->value = strtok(NULL, "=");
604
605     if (new_option->value != NULL) {
606
607         while(isspace(*new_option->value)) {
608             new_option->value++;
609         }
610         
611         for (i = strlen(new_option->value); i > 0; i--) {
612             if (!isspace(new_option->value[i - 1])) break;
613         }
614         
615         new_option->value[i] = '\0';
616         new_option->value = strdup(new_option->value);
617     }
618
619     /* Add to list */
620
621     DLIST_ADD(*options, new_option);
622
623     return True;
624 }
625
626 #endif
627
628
629 /*******************************************************************
630  A wrapper for vfs_chdir().
631 ********************************************************************/
632
633 int vfs_ChDir(connection_struct *conn, char *path)
634 {
635         int res;
636         static pstring LastDir="";
637
638         if (strcsequal(path,"."))
639                 return(0);
640
641         if (*path == '/' && strcsequal(LastDir,path))
642                 return(0);
643
644         DEBUG(3,("vfs_ChDir to %s\n",path));
645
646         res = vfs_chdir(conn,path);
647         if (!res)
648                 pstrcpy(LastDir,path);
649         return(res);
650 }
651
652 /* number of list structures for a caching GetWd function. */
653 #define MAX_GETWDCACHE (50)
654
655 static struct {
656         SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
657         SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
658         char *dos_path; /* The pathname in DOS format. */
659         BOOL valid;
660 } ino_list[MAX_GETWDCACHE];
661
662 extern BOOL use_getwd_cache;
663
664 /****************************************************************************
665  Prompte a ptr (to make it recently used)
666 ****************************************************************************/
667
668 static void array_promote(char *array,int elsize,int element)
669 {
670         char *p;
671         if (element == 0)
672                 return;
673
674         p = (char *)malloc(elsize);
675
676         if (!p) {
677                 DEBUG(5,("array_promote: malloc fail\n"));
678                 return;
679         }
680
681         memcpy(p,array + element * elsize, elsize);
682         memmove(array + elsize,array,elsize*element);
683         memcpy(array,p,elsize);
684         SAFE_FREE(p);
685 }
686
687 /*******************************************************************
688  Return the absolute current directory path - given a UNIX pathname.
689  Note that this path is returned in DOS format, not UNIX
690  format. Note this can be called with conn == NULL.
691 ********************************************************************/
692
693 char *vfs_GetWd(connection_struct *conn, char *path)
694 {
695         pstring s;
696         static BOOL getwd_cache_init = False;
697         SMB_STRUCT_STAT st, st2;
698         int i;
699
700         *s = 0;
701
702         if (!use_getwd_cache)
703                 return(vfs_getwd(conn,path));
704
705         /* init the cache */
706         if (!getwd_cache_init) {
707                 getwd_cache_init = True;
708                 for (i=0;i<MAX_GETWDCACHE;i++) {
709                         string_set(&ino_list[i].dos_path,"");
710                         ino_list[i].valid = False;
711                 }
712         }
713
714         /*  Get the inode of the current directory, if this doesn't work we're
715                 in trouble :-) */
716
717         if (vfs_stat(conn, ".",&st) == -1) {
718                 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
719                 return(vfs_getwd(conn,path));
720         }
721
722
723         for (i=0; i<MAX_GETWDCACHE; i++) {
724                 if (ino_list[i].valid) {
725
726                         /*  If we have found an entry with a matching inode and dev number
727                                 then find the inode number for the directory in the cached string.
728                                 If this agrees with that returned by the stat for the current
729                                 directory then all is o.k. (but make sure it is a directory all
730                                 the same...) */
731
732                         if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
733                                 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
734                                         if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
735                                                         (st2.st_mode & S_IFMT) == S_IFDIR) {
736                                                 pstrcpy (path, ino_list[i].dos_path);
737
738                                                 /* promote it for future use */
739                                                 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
740                                                 return (path);
741                                         } else {
742                                                 /*  If the inode is different then something's changed,
743                                                         scrub the entry and start from scratch. */
744                                                 ino_list[i].valid = False;
745                                         }
746                                 }
747                         }
748                 }
749         }
750
751         /*  We don't have the information to hand so rely on traditional methods.
752                 The very slow getcwd, which spawns a process on some systems, or the
753                 not quite so bad getwd. */
754
755         if (!vfs_getwd(conn,s)) {
756                 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
757                 return (NULL);
758         }
759
760         pstrcpy(path,s);
761
762         DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
763
764         /* add it to the cache */
765         i = MAX_GETWDCACHE - 1;
766         string_set(&ino_list[i].dos_path,s);
767         ino_list[i].dev = st.st_dev;
768         ino_list[i].inode = st.st_ino;
769         ino_list[i].valid = True;
770
771         /* put it at the top of the list */
772         array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
773
774         return (path);
775 }
776
777 /*******************************************************************
778  Reduce a file name, removing .. elements and checking that
779  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
780  on the system that has the referenced file system.
781  Widelinks are allowed if widelinks is true.
782 ********************************************************************/
783
784 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
785 {
786 #ifndef REDUCE_PATHS
787         return True;
788 #else
789         pstring dir2;
790         pstring wd;
791         pstring base_name;
792         pstring newname;
793         char *p=NULL;
794         BOOL relative = (*s != '/');
795
796         *dir2 = *wd = *base_name = *newname = 0;
797
798         if (widelinks) {
799                 unix_clean_name(s);
800                 /* can't have a leading .. */
801                 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
802                         DEBUG(3,("Illegal file name? (%s)\n",s));
803                         return(False);
804                 }
805
806                 if (strlen(s) == 0)
807                         pstrcpy(s,"./");
808
809                 return(True);
810         }
811
812         DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
813
814         /* remove any double slashes */
815         all_string_sub(s,"//","/",0);
816
817         pstrcpy(base_name,s);
818         p = strrchr_m(base_name,'/');
819
820         if (!p)
821                 return(True);
822
823         if (!vfs_GetWd(conn,wd)) {
824                 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
825                 return(False);
826         }
827
828         if (vfs_ChDir(conn,dir) != 0) {
829                 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
830                 return(False);
831         }
832
833         if (!vfs_GetWd(conn,dir2)) {
834                 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
835                 vfs_ChDir(conn,wd);
836                 return(False);
837         }
838
839         if (p && (p != base_name)) {
840                 *p = 0;
841                 if (strcmp(p+1,".")==0)
842                         p[1]=0;
843                 if (strcmp(p+1,"..")==0)
844                         *p = '/';
845         }
846
847         if (vfs_ChDir(conn,base_name) != 0) {
848                 vfs_ChDir(conn,wd);
849                 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
850                 return(False);
851         }
852
853         if (!vfs_GetWd(conn,newname)) {
854                 vfs_ChDir(conn,wd);
855                 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
856                 return(False);
857         }
858
859         if (p && (p != base_name)) {
860                 pstrcat(newname,"/");
861                 pstrcat(newname,p+1);
862         }
863
864         {
865                 size_t l = strlen(dir2);
866                 if (dir2[l-1] == '/')
867                         l--;
868
869                 if (strncmp(newname,dir2,l) != 0) {
870                         vfs_ChDir(conn,wd);
871                         DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
872                         return(False);
873                 }
874
875                 if (relative) {
876                         if (newname[l] == '/')
877                                 pstrcpy(s,newname + l + 1);
878                         else
879                                 pstrcpy(s,newname+l);
880                 } else
881                         pstrcpy(s,newname);
882         }
883
884         vfs_ChDir(conn,wd);
885
886         if (strlen(s) == 0)
887                 pstrcpy(s,"./");
888
889         DEBUG(3,("reduced to %s\n",s));
890         return(True);
891 #endif
892 }