2 Unix SMB/Netbios implementation.
4 VFS initialisation and support functions
5 Copyright (C) Tim Potter 1999
6 Copyright (C) Alexander Bokovoy 2002
7 Copyright (C) James Peach 2006
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 This work was sponsored by Optifacio Software Services, Inc.
28 #define DBGC_CLASS DBGC_VFS
32 struct vfs_init_function_entry {
34 vfs_op_tuple *vfs_op_tuples;
35 struct vfs_init_function_entry *prev, *next;
38 static struct vfs_init_function_entry *backends = NULL;
40 /****************************************************************************
41 maintain the list of available backends
42 ****************************************************************************/
44 static struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
46 struct vfs_init_function_entry *entry = backends;
49 if (strcmp(entry->name, name)==0) return entry;
56 NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tuples)
58 struct vfs_init_function_entry *entry = backends;
60 if ((version != SMB_VFS_INTERFACE_VERSION)) {
61 DEBUG(0, ("Failed to register vfs module.\n"
62 "The module was compiled against SMB_VFS_INTERFACE_VERSION %d,\n"
63 "current SMB_VFS_INTERFACE_VERSION is %d.\n"
64 "Please recompile against the current Samba Version!\n",
65 version, SMB_VFS_INTERFACE_VERSION));
66 return NT_STATUS_OBJECT_TYPE_MISMATCH;
69 if (!name || !name[0] || !vfs_op_tuples) {
70 DEBUG(0,("smb_register_vfs() called with NULL pointer or empty name!\n"));
71 return NT_STATUS_INVALID_PARAMETER;
74 if (vfs_find_backend_entry(name)) {
75 DEBUG(0,("VFS module %s already loaded!\n", name));
76 return NT_STATUS_OBJECT_NAME_COLLISION;
79 entry = SMB_XMALLOC_P(struct vfs_init_function_entry);
80 entry->name = smb_xstrdup(name);
81 entry->vfs_op_tuples = vfs_op_tuples;
83 DLIST_ADD(backends, entry);
84 DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
88 /****************************************************************************
89 initialise default vfs hooks
90 ****************************************************************************/
92 static void vfs_init_default(connection_struct *conn)
94 DEBUG(3, ("Initialising default vfs hooks\n"));
95 vfs_init_custom(conn, DEFAULT_VFS_MODULE_NAME);
98 /****************************************************************************
99 initialise custom vfs hooks
100 ****************************************************************************/
102 static inline void vfs_set_operation(struct vfs_ops * vfs, vfs_op_type which,
103 struct vfs_handle_struct * handle, void * op)
105 ((struct vfs_handle_struct **)&vfs->handles)[which] = handle;
106 ((void **)(void *)&vfs->ops)[which] = op;
109 bool vfs_init_custom(connection_struct *conn, const char *vfs_object)
112 char *module_name = NULL;
113 char *module_param = NULL, *p;
115 vfs_handle_struct *handle;
116 struct vfs_init_function_entry *entry;
118 if (!conn||!vfs_object||!vfs_object[0]) {
119 DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n"));
127 DEBUG(3, ("Initialising custom vfs hooks from [%s]\n", vfs_object));
129 module_name = smb_xstrdup(vfs_object);
131 p = strchr_m(module_name, ':');
136 trim_char(module_param, ' ', ' ');
139 trim_char(module_name, ' ', ' ');
141 /* First, try to load the module with the new module system */
142 if((entry = vfs_find_backend_entry(module_name)) ||
143 (NT_STATUS_IS_OK(smb_probe_module("vfs", module_name)) &&
144 (entry = vfs_find_backend_entry(module_name)))) {
146 DEBUGADD(5,("Successfully loaded vfs module [%s] with the new modules system\n", vfs_object));
148 if ((ops = entry->vfs_op_tuples) == NULL) {
149 DEBUG(0, ("entry->vfs_op_tuples==NULL for [%s] failed\n", vfs_object));
150 SAFE_FREE(module_name);
154 DEBUG(0,("Can't find a vfs module [%s]\n",vfs_object));
155 SAFE_FREE(module_name);
159 handle = TALLOC_ZERO_P(conn->mem_ctx,vfs_handle_struct);
161 DEBUG(0,("TALLOC_ZERO() failed!\n"));
162 SAFE_FREE(module_name);
165 memcpy(&handle->vfs_next, &conn->vfs, sizeof(struct vfs_ops));
168 handle->param = talloc_strdup(conn->mem_ctx, module_param);
170 DLIST_ADD(conn->vfs_handles, handle);
172 for(i=0; ops[i].op != NULL; i++) {
173 DEBUG(5, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
174 if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
175 /* If this operation was already made opaque by different module, it
176 * will be overridden here.
178 DEBUGADD(5, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
179 vfs_set_operation(&conn->vfs_opaque, ops[i].type, handle, ops[i].op);
181 /* Change current VFS disposition*/
182 DEBUGADD(5, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
183 vfs_set_operation(&conn->vfs, ops[i].type, handle, ops[i].op);
186 SAFE_FREE(module_name);
190 /*****************************************************************
191 Allow VFS modules to extend files_struct with VFS-specific state.
192 This will be ok for small numbers of extensions, but might need to
193 be refactored if it becomes more widely used.
194 ******************************************************************/
196 #define EXT_DATA_AREA(e) ((uint8 *)(e) + sizeof(struct vfs_fsp_data))
198 void *vfs_add_fsp_extension_notype(vfs_handle_struct *handle, files_struct *fsp, size_t ext_size)
200 struct vfs_fsp_data *ext;
203 /* Prevent VFS modules adding multiple extensions. */
204 if ((ext_data = vfs_fetch_fsp_extension(handle, fsp))) {
208 ext = (struct vfs_fsp_data *)TALLOC_ZERO(
209 handle->conn->mem_ctx, sizeof(struct vfs_fsp_data) + ext_size);
215 ext->next = fsp->vfs_extension;
216 fsp->vfs_extension = ext;
217 return EXT_DATA_AREA(ext);
220 void vfs_remove_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
222 struct vfs_fsp_data *curr;
223 struct vfs_fsp_data *prev;
225 for (curr = fsp->vfs_extension, prev = NULL;
227 prev = curr, curr = curr->next) {
228 if (curr->owner == handle) {
230 prev->next = curr->next;
232 fsp->vfs_extension = curr->next;
240 void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
242 struct vfs_fsp_data *head;
244 for (head = fsp->vfs_extension; head; head = head->next) {
245 if (head->owner == handle) {
246 return EXT_DATA_AREA(head);
255 /*****************************************************************
257 ******************************************************************/
259 bool smbd_vfs_init(connection_struct *conn)
261 const char **vfs_objects;
265 /* Normal share - initialise with disk access functions */
266 vfs_init_default(conn);
267 vfs_objects = lp_vfs_objects(SNUM(conn));
269 /* Override VFS functions if 'vfs object' was not specified*/
270 if (!vfs_objects || !vfs_objects[0])
273 for (i=0; vfs_objects[i] ;) {
277 for (j=i-1; j >= 0; j--) {
278 if (!vfs_init_custom(conn, vfs_objects[j])) {
279 DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_objects[j]));
286 /*******************************************************************
287 Check if directory exists.
288 ********************************************************************/
290 bool vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
298 if (SMB_VFS_STAT(conn,dname,st) != 0)
301 ret = S_ISDIR(st->st_mode);
308 /*******************************************************************
309 Check if an object exists in the vfs.
310 ********************************************************************/
312 bool vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
321 if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
326 /*******************************************************************
327 Check if a file exists in the vfs.
328 ********************************************************************/
330 bool vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
339 if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
341 return(S_ISREG(sbuf->st_mode));
344 /****************************************************************************
345 Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
346 ****************************************************************************/
348 ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
352 while (total < byte_count)
354 ssize_t ret = SMB_VFS_READ(fsp, fsp->fh->fd, buf + total,
357 if (ret == 0) return total;
366 return (ssize_t)total;
369 ssize_t vfs_pread_data(files_struct *fsp, char *buf,
370 size_t byte_count, SMB_OFF_T offset)
374 while (total < byte_count)
376 ssize_t ret = SMB_VFS_PREAD(fsp, fsp->fh->fd, buf + total,
377 byte_count - total, offset + total);
379 if (ret == 0) return total;
388 return (ssize_t)total;
391 /****************************************************************************
392 Write data to a fd on the vfs.
393 ****************************************************************************/
395 ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
401 ret = SMB_VFS_WRITE(fsp,fsp->fh->fd,buffer + total,N - total);
410 return (ssize_t)total;
413 ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer,
414 size_t N, SMB_OFF_T offset)
420 ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, buffer + total,
421 N - total, offset + total);
430 return (ssize_t)total;
432 /****************************************************************************
433 An allocate file space call using the vfs interface.
434 Allocates space for a file from a filedescriptor.
435 Returns 0 on success, -1 on failure.
436 ****************************************************************************/
438 int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
442 connection_struct *conn = fsp->conn;
443 SMB_BIG_UINT space_avail;
444 SMB_BIG_UINT bsize,dfree,dsize;
446 release_level_2_oplocks_on_change(fsp);
449 * Actually try and commit the space on disk....
452 DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
454 if (((SMB_OFF_T)len) < 0) {
455 DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
460 ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st);
464 if (len == (SMB_BIG_UINT)st.st_size)
467 if (len < (SMB_BIG_UINT)st.st_size) {
468 /* Shrink - use ftruncate. */
470 DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
471 fsp->fsp_name, (double)st.st_size ));
473 flush_write_cache(fsp, SIZECHANGE_FLUSH);
474 if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, (SMB_OFF_T)len)) != -1) {
475 set_filelen_write_cache(fsp, len);
480 /* Grow - we need to test if we have enough space. */
482 if (!lp_strict_allocate(SNUM(fsp->conn)))
486 len /= 1024; /* Len is now number of 1k blocks needed. */
487 space_avail = get_dfree_info(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
488 if (space_avail == (SMB_BIG_UINT)-1) {
492 DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
493 fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
495 if (len > space_avail) {
503 /****************************************************************************
504 A vfs set_filelen call.
505 set the length of a file from a filedescriptor.
506 Returns 0 on success, -1 on failure.
507 ****************************************************************************/
509 int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
513 release_level_2_oplocks_on_change(fsp);
514 DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
515 flush_write_cache(fsp, SIZECHANGE_FLUSH);
516 if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, len)) != -1) {
517 set_filelen_write_cache(fsp, len);
518 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
519 FILE_NOTIFY_CHANGE_SIZE
520 | FILE_NOTIFY_CHANGE_ATTRIBUTES,
527 /****************************************************************************
528 A vfs fill sparse call.
529 Writes zeros from the end of file to len, if len is greater than EOF.
530 Used only by strict_sync.
531 Returns 0 on success, -1 on failure.
532 ****************************************************************************/
534 static char *sparse_buf;
535 #define SPARSE_BUF_WRITE_SIZE (32*1024)
537 int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
546 release_level_2_oplocks_on_change(fsp);
547 ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st);
552 if (len <= st.st_size) {
556 DEBUG(10,("vfs_fill_sparse: write zeros in file %s from len %.0f to len %.0f (%.0f bytes)\n",
557 fsp->fsp_name, (double)st.st_size, (double)len, (double)(len - st.st_size)));
559 flush_write_cache(fsp, SIZECHANGE_FLUSH);
562 sparse_buf = SMB_CALLOC_ARRAY(char, SPARSE_BUF_WRITE_SIZE);
570 num_to_write = len - st.st_size;
573 while (total < num_to_write) {
574 size_t curr_write_size = MIN(SPARSE_BUF_WRITE_SIZE, (num_to_write - total));
576 pwrite_ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, sparse_buf, curr_write_size, offset + total);
577 if (pwrite_ret == -1) {
578 DEBUG(10,("vfs_fill_sparse: SMB_VFS_PWRITE for file %s failed with error %s\n",
579 fsp->fsp_name, strerror(errno) ));
582 if (pwrite_ret == 0) {
589 set_filelen_write_cache(fsp, len);
593 /****************************************************************************
594 Transfer some data (n bytes) between two file_struct's.
595 ****************************************************************************/
597 static files_struct *in_fsp;
598 static files_struct *out_fsp;
600 static ssize_t read_fn(int fd, void *buf, size_t len)
602 return SMB_VFS_READ(in_fsp, fd, buf, len);
605 static ssize_t write_fn(int fd, const void *buf, size_t len)
607 return SMB_VFS_WRITE(out_fsp, fd, buf, len);
610 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
615 return transfer_file_internal(in_fsp->fh->fd, out_fsp->fh->fd, n, read_fn, write_fn);
618 /*******************************************************************
619 A vfs_readdir wrapper which just returns the file name.
620 ********************************************************************/
622 char *vfs_readdirname(connection_struct *conn, void *p)
624 SMB_STRUCT_DIRENT *ptr= NULL;
630 ptr = SMB_VFS_READDIR(conn, (DIR *)p);
641 #ifdef HAVE_BROKEN_READDIR_NAME
642 /* using /usr/ucb/cc is BAD */
649 /*******************************************************************
650 A wrapper for vfs_chdir().
651 ********************************************************************/
653 int vfs_ChDir(connection_struct *conn, const char *path)
656 static char *LastDir = NULL;
659 LastDir = SMB_STRDUP("");
662 if (strcsequal(path,"."))
665 if (*path == '/' && strcsequal(LastDir,path))
668 DEBUG(4,("vfs_ChDir to %s\n",path));
670 res = SMB_VFS_CHDIR(conn,path);
673 LastDir = SMB_STRDUP(path);
678 /* number of list structures for a caching GetWd function. */
679 #define MAX_GETWDCACHE (50)
682 SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
683 SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
684 char *path; /* The pathname. */
686 } ino_list[MAX_GETWDCACHE];
688 extern bool use_getwd_cache;
690 /****************************************************************************
691 Prompte a ptr (to make it recently used)
692 ****************************************************************************/
694 static void array_promote(char *array,int elsize,int element)
700 p = (char *)SMB_MALLOC(elsize);
703 DEBUG(5,("array_promote: malloc fail\n"));
707 memcpy(p,array + element * elsize, elsize);
708 memmove(array + elsize,array,elsize*element);
709 memcpy(array,p,elsize);
713 /*******************************************************************
714 Return the absolute current directory path - given a UNIX pathname.
715 Note that this path is returned in DOS format, not UNIX
716 format. Note this can be called with conn == NULL.
717 ********************************************************************/
719 char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
726 static bool getwd_cache_init = False;
727 SMB_STRUCT_STAT st, st2;
733 if (!use_getwd_cache) {
735 ret = SMB_VFS_GETWD(conn,s);
737 DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, "
738 "errno %s\n",strerror(errno)));
741 return talloc_strdup(ctx, ret);
745 if (!getwd_cache_init) {
746 getwd_cache_init = True;
747 for (i=0;i<MAX_GETWDCACHE;i++) {
748 string_set(&ino_list[i].path,"");
749 ino_list[i].valid = False;
753 /* Get the inode of the current directory, if this doesn't work we're
756 if (SMB_VFS_STAT(conn, ".",&st) == -1) {
757 /* Known to fail for root: the directory may be
758 * NFS-mounted and exported with root_squash (so has no root access). */
759 DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s "
766 for (i=0; i<MAX_GETWDCACHE; i++) {
767 if (ino_list[i].valid) {
769 /* If we have found an entry with a matching inode and dev number
770 then find the inode number for the directory in the cached string.
771 If this agrees with that returned by the stat for the current
772 directory then all is o.k. (but make sure it is a directory all
775 if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
776 if (SMB_VFS_STAT(conn,ino_list[i].path,&st2) == 0) {
777 if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
778 (st2.st_mode & S_IFMT) == S_IFDIR) {
780 ret = talloc_strdup(ctx,
783 /* promote it for future use */
784 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
790 /* If the inode is different then something's changed,
791 scrub the entry and start from scratch. */
792 ino_list[i].valid = False;
799 /* We don't have the information to hand so rely on traditional
800 * methods. The very slow getcwd, which spawns a process on some
801 * systems, or the not quite so bad getwd. */
803 if (!SMB_VFS_GETWD(conn,s)) {
804 DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",
809 ret = talloc_strdup(ctx,s);
811 DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",
812 s,(double)st.st_ino,(double)st.st_dev));
814 /* add it to the cache */
815 i = MAX_GETWDCACHE - 1;
816 string_set(&ino_list[i].path,s);
817 ino_list[i].dev = st.st_dev;
818 ino_list[i].inode = st.st_ino;
819 ino_list[i].valid = True;
821 /* put it at the top of the list */
822 array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
830 /*******************************************************************
831 Reduce a file name, removing .. elements and checking that
832 it is below dir in the heirachy. This uses realpath.
833 ********************************************************************/
835 NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
837 #ifdef REALPATH_TAKES_NULL
838 bool free_resolved_name = True;
841 char resolved_name_buf[PATH_MAX+1];
843 pstring resolved_name_buf;
845 bool free_resolved_name = False;
847 char *resolved_name = NULL;
848 size_t con_path_len = strlen(conn->connectpath);
851 DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath));
853 #ifdef REALPATH_TAKES_NULL
854 resolved_name = SMB_VFS_REALPATH(conn,fname,NULL);
856 resolved_name = SMB_VFS_REALPATH(conn,fname,resolved_name_buf);
859 if (!resolved_name) {
862 DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname));
863 return map_nt_error_from_unix(errno);
866 TALLOC_CTX *tmp_ctx = talloc_stackframe();
867 char *tmp_fname = NULL;
868 char *last_component = NULL;
869 /* Last component didn't exist. Remove it and try and canonicalise the directory. */
871 tmp_fname = talloc_strdup(tmp_ctx, fname);
873 TALLOC_FREE(tmp_ctx);
874 return NT_STATUS_NO_MEMORY;
876 p = strrchr_m(tmp_fname, '/');
881 last_component = tmp_fname;
882 tmp_fname = talloc_strdup(tmp_ctx,
885 TALLOC_FREE(tmp_ctx);
886 return NT_STATUS_NO_MEMORY;
890 #ifdef REALPATH_TAKES_NULL
891 resolved_name = SMB_VFS_REALPATH(conn,tmp_fname,NULL);
893 resolved_name = SMB_VFS_REALPATH(conn,tmp_fname,resolved_name_buf);
895 if (!resolved_name) {
896 DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname));
897 TALLOC_FREE(tmp_ctx);
898 return map_nt_error_from_unix(errno);
900 tmp_fname = talloc_asprintf(tmp_ctx,
905 TALLOC_FREE(tmp_ctx);
906 return NT_STATUS_NO_MEMORY;
908 #ifdef REALPATH_TAKES_NULL
909 SAFE_FREE(resolved_name);
910 resolved_name = SMB_STRDUP(tmp_fname);
911 if (!resolved_name) {
912 DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname));
913 return NT_STATUS_NO_MEMORY;
917 safe_strcpy(resolved_name_buf, tmp_fname, PATH_MAX);
919 pstrcpy(resolved_name_buf, tmp_fname);
921 resolved_name = resolved_name_buf;
923 TALLOC_FREE(tmp_ctx);
927 DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname));
928 return map_nt_error_from_unix(errno);
932 DEBUG(10,("reduce_name realpath [%s] -> [%s]\n", fname, resolved_name));
934 if (*resolved_name != '/') {
935 DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n"));
936 if (free_resolved_name) {
937 SAFE_FREE(resolved_name);
939 return NT_STATUS_OBJECT_NAME_INVALID;
942 /* Check for widelinks allowed. */
943 if (!lp_widelinks(SNUM(conn)) && (strncmp(conn->connectpath, resolved_name, con_path_len) != 0)) {
944 DEBUG(2, ("reduce_name: Bad access attempt: %s is a symlink outside the share path", fname));
945 if (free_resolved_name) {
946 SAFE_FREE(resolved_name);
948 return NT_STATUS_ACCESS_DENIED;
951 /* Check if we are allowing users to follow symlinks */
952 /* Patch from David Clerc <David.Clerc@cui.unige.ch>
953 University of Geneva */
956 if (!lp_symlinks(SNUM(conn))) {
957 SMB_STRUCT_STAT statbuf;
958 if ( (SMB_VFS_LSTAT(conn,fname,&statbuf) != -1) &&
959 (S_ISLNK(statbuf.st_mode)) ) {
960 if (free_resolved_name) {
961 SAFE_FREE(resolved_name);
963 DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",resolved_name));
964 return NT_STATUS_ACCESS_DENIED;
969 DEBUG(3,("reduce_name: %s reduced to %s\n", fname, resolved_name));
970 if (free_resolved_name) {
971 SAFE_FREE(resolved_name);