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