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