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