sync'ing up for 3.0alpha20 release
[kai/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                  * Check if high bits should have been set,
324                  * then (if bits are missing): add them.
325                  * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
326                  */
327                 if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
328                                 !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
329                         vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
330         }
331         return ret;
332 }
333
334 /*******************************************************************
335  Check if an object exists in the vfs.
336 ********************************************************************/
337
338 BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
339 {
340         SMB_STRUCT_STAT st;
341
342         if (!sbuf)
343                 sbuf = &st;
344
345         ZERO_STRUCTP(sbuf);
346
347         if (vfs_stat(conn,fname,sbuf) == -1)
348                 return(False);
349         return True;
350 }
351
352 /*******************************************************************
353  Check if a file exists in the vfs.
354 ********************************************************************/
355
356 BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
357 {
358         SMB_STRUCT_STAT st;
359
360         if (!sbuf)
361                 sbuf = &st;
362
363         ZERO_STRUCTP(sbuf);
364
365         if (vfs_stat(conn,fname,sbuf) == -1)
366                 return False;
367         return(S_ISREG(sbuf->st_mode));
368 }
369
370 /****************************************************************************
371  Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
372 ****************************************************************************/
373
374 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
375 {
376         size_t total=0;
377
378         while (total < byte_count)
379         {
380                 ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total,
381                                         byte_count - total);
382
383                 if (ret == 0) return total;
384                 if (ret == -1) {
385                         if (errno == EINTR)
386                                 continue;
387                         else
388                                 return -1;
389                 }
390                 total += ret;
391         }
392         return (ssize_t)total;
393 }
394
395 /****************************************************************************
396  Write data to a fd on the vfs.
397 ****************************************************************************/
398
399 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
400 {
401         size_t total=0;
402         ssize_t ret;
403
404         while (total < N) {
405                 ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total);
406
407                 if (ret == -1)
408                         return -1;
409                 if (ret == 0)
410                         return total;
411
412                 total += ret;
413         }
414         return (ssize_t)total;
415 }
416
417 /****************************************************************************
418  An allocate file space call using the vfs interface.
419  Allocates space for a file from a filedescriptor.
420  Returns 0 on success, -1 on failure.
421 ****************************************************************************/
422
423 int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len)
424 {
425         int ret;
426         SMB_STRUCT_STAT st;
427         connection_struct *conn = fsp->conn;
428         struct vfs_ops *vfs_ops = &conn->vfs_ops;
429         SMB_OFF_T space_avail;
430         SMB_BIG_UINT bsize,dfree,dsize;
431
432         release_level_2_oplocks_on_change(fsp);
433
434         /*
435          * Actually try and commit the space on disk....
436          */
437
438         DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
439
440         ret = vfs_fstat(fsp,fsp->fd,&st);
441         if (ret == -1)
442                 return ret;
443
444         if (len == st.st_size)
445                 return 0;
446
447         if (len < st.st_size) {
448                 /* Shrink - use ftruncate. */
449
450                 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
451                                 fsp->fsp_name, (double)st.st_size ));
452
453                 flush_write_cache(fsp, SIZECHANGE_FLUSH);
454                 if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) {
455                         set_filelen_write_cache(fsp, len);
456                 }
457                 return ret;
458         }
459
460         /* Grow - we need to test if we have enough space. */
461
462         if (!lp_strict_allocate(SNUM(fsp->conn)))
463                 return 0;
464
465         len -= st.st_size;
466         len /= 1024; /* Len is now number of 1k blocks needed. */
467         space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
468
469         DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n",
470                         fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail ));
471
472         if (len > space_avail) {
473                 errno = ENOSPC;
474                 return -1;
475         }
476
477         return 0;
478 }
479
480 /****************************************************************************
481  A vfs set_filelen call.
482  set the length of a file from a filedescriptor.
483  Returns 0 on success, -1 on failure.
484 ****************************************************************************/
485
486 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
487 {
488         int ret;
489
490         release_level_2_oplocks_on_change(fsp);
491         DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
492         flush_write_cache(fsp, SIZECHANGE_FLUSH);
493         if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1)
494                 set_filelen_write_cache(fsp, len);
495
496         return ret;
497 }
498
499 /****************************************************************************
500  Transfer some data (n bytes) between two file_struct's.
501 ****************************************************************************/
502
503 static files_struct *in_fsp;
504 static files_struct *out_fsp;
505
506 static ssize_t read_fn(int fd, void *buf, size_t len)
507 {
508         return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len);
509 }
510
511 static ssize_t write_fn(int fd, const void *buf, size_t len)
512 {
513         return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len);
514 }
515
516 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
517 {
518         in_fsp = in;
519         out_fsp = out;
520
521         return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
522 }
523
524 /*******************************************************************
525  A vfs_readdir wrapper which just returns the file name.
526 ********************************************************************/
527
528 char *vfs_readdirname(connection_struct *conn, void *p)
529 {
530         struct dirent *ptr;
531         char *dname;
532
533         if (!p)
534                 return(NULL);
535
536         ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p);
537         if (!ptr)
538                 return(NULL);
539
540         dname = ptr->d_name;
541
542 #ifdef NEXT2
543         if (telldir(p) < 0)
544                 return(NULL);
545 #endif
546
547 #ifdef HAVE_BROKEN_READDIR
548         /* using /usr/ucb/cc is BAD */
549         dname = dname - 2;
550 #endif
551
552         return(dname);
553 }
554
555 /* VFS options not quite working yet */
556
557 #if 0
558
559 /***************************************************************************
560   handle the interpretation of the vfs option parameter
561  *************************************************************************/
562 static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
563 {
564     struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
565     int i;
566
567     /* Create new vfs option */
568
569     new_option = (struct vfs_options *)malloc(sizeof(*new_option));
570     if (new_option == NULL) {
571         return False;
572     }
573
574     ZERO_STRUCTP(new_option);
575
576     /* Get name and value */
577
578     new_option->name = strtok(pszParmValue, "=");
579
580     if (new_option->name == NULL) {
581         return False;
582     }
583
584     while(isspace(*new_option->name)) {
585         new_option->name++;
586     }
587
588     for (i = strlen(new_option->name); i > 0; i--) {
589         if (!isspace(new_option->name[i - 1])) break;
590     }
591
592     new_option->name[i] = '\0';
593     new_option->name = strdup(new_option->name);
594
595     new_option->value = strtok(NULL, "=");
596
597     if (new_option->value != NULL) {
598
599         while(isspace(*new_option->value)) {
600             new_option->value++;
601         }
602         
603         for (i = strlen(new_option->value); i > 0; i--) {
604             if (!isspace(new_option->value[i - 1])) break;
605         }
606         
607         new_option->value[i] = '\0';
608         new_option->value = strdup(new_option->value);
609     }
610
611     /* Add to list */
612
613     DLIST_ADD(*options, new_option);
614
615     return True;
616 }
617
618 #endif
619
620
621 /*******************************************************************
622  A wrapper for vfs_chdir().
623 ********************************************************************/
624
625 int vfs_ChDir(connection_struct *conn, char *path)
626 {
627         int res;
628         static pstring LastDir="";
629
630         if (strcsequal(path,"."))
631                 return(0);
632
633         if (*path == '/' && strcsequal(LastDir,path))
634                 return(0);
635
636         DEBUG(3,("vfs_ChDir to %s\n",path));
637
638         res = vfs_chdir(conn,path);
639         if (!res)
640                 pstrcpy(LastDir,path);
641         return(res);
642 }
643
644 /* number of list structures for a caching GetWd function. */
645 #define MAX_GETWDCACHE (50)
646
647 static struct {
648         SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
649         SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
650         char *dos_path; /* The pathname in DOS format. */
651         BOOL valid;
652 } ino_list[MAX_GETWDCACHE];
653
654 extern BOOL use_getwd_cache;
655
656 /****************************************************************************
657  Prompte a ptr (to make it recently used)
658 ****************************************************************************/
659
660 static void array_promote(char *array,int elsize,int element)
661 {
662         char *p;
663         if (element == 0)
664                 return;
665
666         p = (char *)malloc(elsize);
667
668         if (!p) {
669                 DEBUG(5,("array_promote: malloc fail\n"));
670                 return;
671         }
672
673         memcpy(p,array + element * elsize, elsize);
674         memmove(array + elsize,array,elsize*element);
675         memcpy(array,p,elsize);
676         SAFE_FREE(p);
677 }
678
679 /*******************************************************************
680  Return the absolute current directory path - given a UNIX pathname.
681  Note that this path is returned in DOS format, not UNIX
682  format. Note this can be called with conn == NULL.
683 ********************************************************************/
684
685 char *vfs_GetWd(connection_struct *conn, char *path)
686 {
687         pstring s;
688         static BOOL getwd_cache_init = False;
689         SMB_STRUCT_STAT st, st2;
690         int i;
691
692         *s = 0;
693
694         if (!use_getwd_cache)
695                 return(vfs_getwd(conn,path));
696
697         /* init the cache */
698         if (!getwd_cache_init) {
699                 getwd_cache_init = True;
700                 for (i=0;i<MAX_GETWDCACHE;i++) {
701                         string_set(&ino_list[i].dos_path,"");
702                         ino_list[i].valid = False;
703                 }
704         }
705
706         /*  Get the inode of the current directory, if this doesn't work we're
707                 in trouble :-) */
708
709         if (vfs_stat(conn, ".",&st) == -1) {
710                 DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
711                 return(vfs_getwd(conn,path));
712         }
713
714
715         for (i=0; i<MAX_GETWDCACHE; i++) {
716                 if (ino_list[i].valid) {
717
718                         /*  If we have found an entry with a matching inode and dev number
719                                 then find the inode number for the directory in the cached string.
720                                 If this agrees with that returned by the stat for the current
721                                 directory then all is o.k. (but make sure it is a directory all
722                                 the same...) */
723
724                         if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
725                                 if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) {
726                                         if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
727                                                         (st2.st_mode & S_IFMT) == S_IFDIR) {
728                                                 pstrcpy (path, ino_list[i].dos_path);
729
730                                                 /* promote it for future use */
731                                                 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
732                                                 return (path);
733                                         } else {
734                                                 /*  If the inode is different then something's changed,
735                                                         scrub the entry and start from scratch. */
736                                                 ino_list[i].valid = False;
737                                         }
738                                 }
739                         }
740                 }
741         }
742
743         /*  We don't have the information to hand so rely on traditional methods.
744                 The very slow getcwd, which spawns a process on some systems, or the
745                 not quite so bad getwd. */
746
747         if (!vfs_getwd(conn,s)) {
748                 DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno)));
749                 return (NULL);
750         }
751
752         pstrcpy(path,s);
753
754         DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
755
756         /* add it to the cache */
757         i = MAX_GETWDCACHE - 1;
758         string_set(&ino_list[i].dos_path,s);
759         ino_list[i].dev = st.st_dev;
760         ino_list[i].inode = st.st_ino;
761         ino_list[i].valid = True;
762
763         /* put it at the top of the list */
764         array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
765
766         return (path);
767 }
768
769 /*******************************************************************
770  Reduce a file name, removing .. elements and checking that
771  it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
772  on the system that has the referenced file system.
773  Widelinks are allowed if widelinks is true.
774 ********************************************************************/
775
776 BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks)
777 {
778 #ifndef REDUCE_PATHS
779         return True;
780 #else
781         pstring dir2;
782         pstring wd;
783         pstring base_name;
784         pstring newname;
785         char *p=NULL;
786         BOOL relative = (*s != '/');
787
788         *dir2 = *wd = *base_name = *newname = 0;
789
790         if (widelinks) {
791                 unix_clean_name(s);
792                 /* can't have a leading .. */
793                 if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) {
794                         DEBUG(3,("Illegal file name? (%s)\n",s));
795                         return(False);
796                 }
797
798                 if (strlen(s) == 0)
799                         pstrcpy(s,"./");
800
801                 return(True);
802         }
803
804         DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
805
806         /* remove any double slashes */
807         all_string_sub(s,"//","/",0);
808
809         pstrcpy(base_name,s);
810         p = strrchr_m(base_name,'/');
811
812         if (!p)
813                 return(True);
814
815         if (!vfs_GetWd(conn,wd)) {
816                 DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
817                 return(False);
818         }
819
820         if (vfs_ChDir(conn,dir) != 0) {
821                 DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
822                 return(False);
823         }
824
825         if (!vfs_GetWd(conn,dir2)) {
826                 DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
827                 vfs_ChDir(conn,wd);
828                 return(False);
829         }
830
831         if (p && (p != base_name)) {
832                 *p = 0;
833                 if (strcmp(p+1,".")==0)
834                         p[1]=0;
835                 if (strcmp(p+1,"..")==0)
836                         *p = '/';
837         }
838
839         if (vfs_ChDir(conn,base_name) != 0) {
840                 vfs_ChDir(conn,wd);
841                 DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
842                 return(False);
843         }
844
845         if (!vfs_GetWd(conn,newname)) {
846                 vfs_ChDir(conn,wd);
847                 DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
848                 return(False);
849         }
850
851         if (p && (p != base_name)) {
852                 pstrcat(newname,"/");
853                 pstrcat(newname,p+1);
854         }
855
856         {
857                 size_t l = strlen(dir2);
858                 if (dir2[l-1] == '/')
859                         l--;
860
861                 if (strncmp(newname,dir2,l) != 0) {
862                         vfs_ChDir(conn,wd);
863                         DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
864                         return(False);
865                 }
866
867                 if (relative) {
868                         if (newname[l] == '/')
869                                 pstrcpy(s,newname + l + 1);
870                         else
871                                 pstrcpy(s,newname+l);
872                 } else
873                         pstrcpy(s,newname);
874         }
875
876         vfs_ChDir(conn,wd);
877
878         if (strlen(s) == 0)
879                 pstrcpy(s,"./");
880
881         DEBUG(3,("reduced to %s\n",s));
882         return(True);
883 #endif
884 }