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