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