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