VFS initialisation and support functions
Copyright (C) Tim Potter 1999
Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) James Peach 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
static struct vfs_init_function_entry *backends = NULL;
-/* Some structures to help us initialise the vfs operations table */
-
-struct vfs_syminfo {
- char *name;
- void *fptr;
-};
-
-/* Default vfs hooks. WARNING: The order of these initialisers is
- very important. They must be in the same order as defined in
- vfs.h. Change at your own peril. */
-
-static struct vfs_ops default_vfs = {
-
- {
- /* Disk operations */
-
- vfswrap_dummy_connect,
- vfswrap_dummy_disconnect,
- vfswrap_disk_free,
- vfswrap_get_quota,
- vfswrap_set_quota,
- vfswrap_get_shadow_copy_data,
- vfswrap_statvfs,
-
- /* Directory operations */
-
- vfswrap_opendir,
- vfswrap_readdir,
- vfswrap_seekdir,
- vfswrap_telldir,
- vfswrap_rewinddir,
- vfswrap_mkdir,
- vfswrap_rmdir,
- vfswrap_closedir,
-
- /* File operations */
-
- vfswrap_open,
- vfswrap_close,
- vfswrap_read,
- vfswrap_pread,
- vfswrap_write,
- vfswrap_pwrite,
- vfswrap_lseek,
- vfswrap_sendfile,
- vfswrap_rename,
- vfswrap_fsync,
- vfswrap_stat,
- vfswrap_fstat,
- vfswrap_lstat,
- vfswrap_unlink,
- vfswrap_chmod,
- vfswrap_fchmod,
- vfswrap_chown,
- vfswrap_fchown,
- vfswrap_chdir,
- vfswrap_getwd,
- vfswrap_utime,
- vfswrap_ftruncate,
- vfswrap_lock,
- vfswrap_getlock,
- vfswrap_symlink,
- vfswrap_readlink,
- vfswrap_link,
- vfswrap_mknod,
- vfswrap_realpath,
-
- /* Windows ACL operations. */
- vfswrap_fget_nt_acl,
- vfswrap_get_nt_acl,
- vfswrap_fset_nt_acl,
- vfswrap_set_nt_acl,
-
- /* POSIX ACL operations. */
- vfswrap_chmod_acl,
- vfswrap_fchmod_acl,
-
- vfswrap_sys_acl_get_entry,
- vfswrap_sys_acl_get_tag_type,
- vfswrap_sys_acl_get_permset,
- vfswrap_sys_acl_get_qualifier,
- vfswrap_sys_acl_get_file,
- vfswrap_sys_acl_get_fd,
- vfswrap_sys_acl_clear_perms,
- vfswrap_sys_acl_add_perm,
- vfswrap_sys_acl_to_text,
- vfswrap_sys_acl_init,
- vfswrap_sys_acl_create_entry,
- vfswrap_sys_acl_set_tag_type,
- vfswrap_sys_acl_set_qualifier,
- vfswrap_sys_acl_set_permset,
- vfswrap_sys_acl_valid,
- vfswrap_sys_acl_set_file,
- vfswrap_sys_acl_set_fd,
- vfswrap_sys_acl_delete_def_file,
- vfswrap_sys_acl_get_perm,
- vfswrap_sys_acl_free_text,
- vfswrap_sys_acl_free_acl,
- vfswrap_sys_acl_free_qualifier,
-
- /* EA operations. */
- vfswrap_getxattr,
- vfswrap_lgetxattr,
- vfswrap_fgetxattr,
- vfswrap_listxattr,
- vfswrap_llistxattr,
- vfswrap_flistxattr,
- vfswrap_removexattr,
- vfswrap_lremovexattr,
- vfswrap_fremovexattr,
- vfswrap_setxattr,
- vfswrap_lsetxattr,
- vfswrap_fsetxattr,
-
- /* AIO operations. */
- vfswrap_aio_read,
- vfswrap_aio_write,
- vfswrap_aio_return,
- vfswrap_aio_cancel,
- vfswrap_aio_error,
- vfswrap_aio_fsync,
- vfswrap_aio_suspend
- }
-};
-
/****************************************************************************
maintain the list of available backends
****************************************************************************/
static void vfs_init_default(connection_struct *conn)
{
DEBUG(3, ("Initialising default vfs hooks\n"));
-
- memcpy(&conn->vfs.ops, &default_vfs.ops, sizeof(default_vfs.ops));
- memcpy(&conn->vfs_opaque.ops, &default_vfs.ops, sizeof(default_vfs.ops));
+ vfs_init_custom(conn, DEFAULT_VFS_MODULE_NAME);
}
/****************************************************************************
initialise custom vfs hooks
****************************************************************************/
+static inline void vfs_set_operation(struct vfs_ops * vfs, vfs_op_type which,
+ struct vfs_handle_struct * handle, void * op)
+{
+ ((struct vfs_handle_struct **)&vfs->handles)[which] = handle;
+ ((void **)(void *)&vfs->ops)[which] = op;
+}
+
BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
{
vfs_op_tuple *ops;
for(i=0; ops[i].op != NULL; i++) {
DEBUG(5, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
- /* Check whether this operation was already made opaque by different module */
- if(((void**)&conn->vfs_opaque.ops)[ops[i].type] == ((void**)&default_vfs.ops)[ops[i].type]) {
- /* No, it isn't overloaded yet. Overload. */
- DEBUGADD(5, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
- ((void**)&conn->vfs_opaque.ops)[ops[i].type] = ops[i].op;
- ((vfs_handle_struct **)&conn->vfs_opaque.handles)[ops[i].type] = handle;
- }
+ /* If this operation was already made opaque by different module, it
+ * will be overridded here.
+ */
+ DEBUGADD(5, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
+ vfs_set_operation(&conn->vfs_opaque, ops[i].type, handle, ops[i].op);
}
/* Change current VFS disposition*/
DEBUGADD(5, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
- ((void**)&conn->vfs.ops)[ops[i].type] = ops[i].op;
- ((vfs_handle_struct **)&conn->vfs.handles)[ops[i].type] = handle;
+ vfs_set_operation(&conn->vfs, ops[i].type, handle, ops[i].op);
}
SAFE_FREE(module_name);
return True;
}
+/*****************************************************************
+ Allow VFS modules to extend files_struct with VFS-specific state.
+ This will be ok for small numbers of extensions, but might need to
+ be refactored if it becomes more widely used.
+******************************************************************/
+
+#define EXT_DATA_AREA(e) ((uint8 *)(e) + sizeof(struct vfs_fsp_data))
+
+void *vfs_add_fsp_extension_notype(vfs_handle_struct *handle, files_struct *fsp, size_t ext_size)
+{
+ struct vfs_fsp_data *ext;
+ void * ext_data;
+
+ /* Prevent VFS modules adding multiple extensions. */
+ if ((ext_data = vfs_fetch_fsp_extension(handle, fsp))) {
+ return ext_data;
+ }
+
+ ext = (struct vfs_fsp_data *)TALLOC_ZERO(
+ handle->conn->mem_ctx, sizeof(struct vfs_fsp_data) + ext_size);
+ if (ext == NULL) {
+ return NULL;
+ }
+
+ ext->owner = handle;
+ ext->next = fsp->vfs_extension;
+ fsp->vfs_extension = ext;
+ return EXT_DATA_AREA(ext);
+}
+
+void vfs_remove_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
+{
+ struct vfs_fsp_data *curr;
+ struct vfs_fsp_data *prev;
+
+ for (curr = fsp->vfs_extension, prev = NULL;
+ curr;
+ prev = curr, curr = curr->next) {
+ if (curr->owner == handle) {
+ if (prev) {
+ prev->next = curr->next;
+ } else {
+ fsp->vfs_extension = curr->next;
+ }
+ TALLOC_FREE(curr);
+ return;
+ }
+ }
+}
+
+void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
+{
+ struct vfs_fsp_data *head;
+
+ for (head = fsp->vfs_extension; head; head = head->next) {
+ if (head->owner == handle) {
+ return EXT_DATA_AREA(head);
+ }
+ }
+
+ return NULL;
+}
+
+#undef EXT_DATA_AREA
+
/*****************************************************************
Generic VFS init.
******************************************************************/
return ret;
}
-/*******************************************************************
- vfs mkdir wrapper
-********************************************************************/
-
-int vfs_MkDir(connection_struct *conn, const char *name, mode_t mode)
-{
- int ret;
- SMB_STRUCT_STAT sbuf;
-
- if(!(ret=SMB_VFS_MKDIR(conn, name, mode))) {
-
- inherit_access_acl(conn, name, mode);
-
- /*
- * Check if high bits should have been set,
- * then (if bits are missing): add them.
- * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
- */
- if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
- !SMB_VFS_STAT(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
- SMB_VFS_CHMOD(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
- }
- return ret;
-}
-
/*******************************************************************
Check if an object exists in the vfs.
********************************************************************/
if (((SMB_OFF_T)len) < 0) {
DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
+ errno = EINVAL;
return -1;
}
if (!p)
return(NULL);
- ptr = SMB_VFS_READDIR(conn,p);
+ ptr = SMB_VFS_READDIR(conn, (DIR *)p);
if (!ptr)
return(NULL);
return (path);
}
-BOOL canonicalize_path(connection_struct *conn, pstring path)
-{
-#ifdef REALPATH_TAKES_NULL
- char *resolved_name = SMB_VFS_REALPATH(conn,path,NULL);
- if (!resolved_name) {
- return False;
- }
- pstrcpy(path, resolved_name);
- SAFE_FREE(resolved_name);
- return True;
-#else
-#ifdef PATH_MAX
- char resolved_name_buf[PATH_MAX+1];
-#else
- pstring resolved_name_buf;
-#endif
- char *resolved_name = SMB_VFS_REALPATH(conn,path,resolved_name_buf);
- if (!resolved_name) {
- return False;
- }
- pstrcpy(path, resolved_name);
- return True;
-#endif /* REALPATH_TAKES_NULL */
-}
-
/*******************************************************************
Reduce a file name, removing .. elements and checking that
it is below dir in the heirachy. This uses realpath.
********************************************************************/
-BOOL reduce_name(connection_struct *conn, const pstring fname)
+NTSTATUS reduce_name(connection_struct *conn, const pstring fname)
{
#ifdef REALPATH_TAKES_NULL
BOOL free_resolved_name = True;
char *resolved_name = NULL;
size_t con_path_len = strlen(conn->connectpath);
char *p = NULL;
- int saved_errno = errno;
DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath));
switch (errno) {
case ENOTDIR:
DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname));
- errno = saved_errno;
- return False;
+ return map_nt_error_from_unix(errno);
case ENOENT:
{
pstring tmp_fname;
#endif
if (!resolved_name) {
DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname));
- errno = saved_errno;
- return False;
+ return map_nt_error_from_unix(errno);
}
pstrcpy(tmp_fname, resolved_name);
pstrcat(tmp_fname, "/");
resolved_name = SMB_STRDUP(tmp_fname);
if (!resolved_name) {
DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname));
- errno = saved_errno;
- return False;
+ return NT_STATUS_NO_MEMORY;
}
#else
#ifdef PATH_MAX
}
default:
DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname));
- /* Don't restore the saved errno. We need to return the error that
- realpath caused here as it was not one of the cases we handle. JRA. */
- return False;
+ return map_nt_error_from_unix(errno);
}
}
if (*resolved_name != '/') {
DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n"));
- if (free_resolved_name)
+ if (free_resolved_name) {
SAFE_FREE(resolved_name);
- errno = saved_errno;
- return False;
+ }
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
/* Check for widelinks allowed. */
if (!lp_widelinks(SNUM(conn)) && (strncmp(conn->connectpath, resolved_name, con_path_len) != 0)) {
DEBUG(2, ("reduce_name: Bad access attempt: %s is a symlink outside the share path", fname));
- if (free_resolved_name)
+ if (free_resolved_name) {
SAFE_FREE(resolved_name);
- errno = EACCES;
- return False;
+ }
+ return NT_STATUS_ACCESS_DENIED;
}
/* Check if we are allowing users to follow symlinks */
SMB_STRUCT_STAT statbuf;
if ( (SMB_VFS_LSTAT(conn,fname,&statbuf) != -1) &&
(S_ISLNK(statbuf.st_mode)) ) {
- if (free_resolved_name)
+ if (free_resolved_name) {
SAFE_FREE(resolved_name);
+ }
DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",resolved_name));
- errno = EACCES;
- return False;
+ return NT_STATUS_ACCESS_DENIED;
}
}
#endif
DEBUG(3,("reduce_name: %s reduced to %s\n", fname, resolved_name));
- if (free_resolved_name)
+ if (free_resolved_name) {
SAFE_FREE(resolved_name);
- errno = saved_errno;
- return(True);
+ }
+ return NT_STATUS_OK;
}