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