*/
#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "memcache.h"
+#include "transfer_file.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_VFS
int vfs_allocate_file_space(files_struct *fsp, uint64_t len)
{
int ret;
- SMB_STRUCT_STAT st;
connection_struct *conn = fsp->conn;
uint64_t space_avail;
uint64_t bsize,dfree,dsize;
+ NTSTATUS status;
/*
* Actually try and commit the space on disk....
return -1;
}
- ret = SMB_VFS_FSTAT(fsp, &st);
- if (ret == -1)
- return ret;
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
- if (len == (uint64_t)st.st_ex_size)
+ if (len == (uint64_t)fsp->fsp_name->st.st_ex_size)
return 0;
- if (len < (uint64_t)st.st_ex_size) {
+ if (len < (uint64_t)fsp->fsp_name->st.st_ex_size) {
/* Shrink - use ftruncate. */
DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current "
"size %.0f\n", fsp_str_dbg(fsp),
- (double)st.st_ex_size));
+ (double)fsp->fsp_name->st.st_ex_size));
contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_SHRINK);
return ret;
}
+ if (!lp_strict_allocate(SNUM(fsp->conn)))
+ return 0;
+
/* Grow - we need to test if we have enough space. */
contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_GROW);
+
+ /* See if we have a syscall that will allocate beyond end-of-file
+ without changing EOF. */
+ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_KEEP_SIZE, 0, len);
+
contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_ALLOC_GROW);
- if (!lp_strict_allocate(SNUM(fsp->conn)))
+ if (ret == 0) {
+ /* We changed the allocation size on disk, but not
+ EOF - exactly as required. We're done ! */
return 0;
+ }
- len -= st.st_ex_size;
+ len -= fsp->fsp_name->st.st_ex_size;
len /= 1024; /* Len is now number of 1k blocks needed. */
space_avail = get_dfree_info(conn, fsp->fsp_name->base_name, false,
&bsize, &dfree, &dsize);
DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, "
"needed blocks = %.0f, space avail = %.0f\n",
- fsp_str_dbg(fsp), (double)st.st_ex_size, (double)len,
+ fsp_str_dbg(fsp), (double)fsp->fsp_name->st.st_ex_size, (double)len,
(double)space_avail));
if (len > space_avail) {
return ret;
}
+/****************************************************************************
+ A slow version of fallocate. Fallback code if SMB_VFS_FALLOCATE
+ fails. Needs to be outside of the default version of SMB_VFS_FALLOCATE
+ as this is also called from the default SMB_VFS_FTRUNCATE code.
+ Always extends the file size.
+ Returns 0 on success, errno on failure.
+****************************************************************************/
+
+#define SPARSE_BUF_WRITE_SIZE (32*1024)
+
+int vfs_slow_fallocate(files_struct *fsp, SMB_OFF_T offset, SMB_OFF_T len)
+{
+ ssize_t pwrite_ret;
+ size_t total = 0;
+
+ if (!sparse_buf) {
+ sparse_buf = SMB_CALLOC_ARRAY(char, SPARSE_BUF_WRITE_SIZE);
+ if (!sparse_buf) {
+ errno = ENOMEM;
+ return ENOMEM;
+ }
+ }
+
+ while (total < len) {
+ size_t curr_write_size = MIN(SPARSE_BUF_WRITE_SIZE, (len - total));
+
+ pwrite_ret = SMB_VFS_PWRITE(fsp, sparse_buf, curr_write_size, offset + total);
+ if (pwrite_ret == -1) {
+ DEBUG(10,("vfs_slow_fallocate: SMB_VFS_PWRITE for file "
+ "%s failed with error %s\n",
+ fsp_str_dbg(fsp), strerror(errno)));
+ return errno;
+ }
+ total += pwrite_ret;
+ }
+
+ return 0;
+}
+
/****************************************************************************
A vfs fill sparse call.
Writes zeros from the end of file to len, if len is greater than EOF.
Returns 0 on success, -1 on failure.
****************************************************************************/
-#define SPARSE_BUF_WRITE_SIZE (32*1024)
-
int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
{
int ret;
- SMB_STRUCT_STAT st;
+ NTSTATUS status;
SMB_OFF_T offset;
- size_t total;
size_t num_to_write;
- ssize_t pwrite_ret;
- ret = SMB_VFS_FSTAT(fsp, &st);
- if (ret == -1) {
- return ret;
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
}
- if (len <= st.st_ex_size) {
+ if (len <= fsp->fsp_name->st.st_ex_size) {
return 0;
}
+#ifdef S_ISFIFO
+ if (S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
+ return 0;
+ }
+#endif
+
DEBUG(10,("vfs_fill_sparse: write zeros in file %s from len %.0f to "
"len %.0f (%.0f bytes)\n", fsp_str_dbg(fsp),
- (double)st.st_ex_size, (double)len,
- (double)(len - st.st_ex_size)));
+ (double)fsp->fsp_name->st.st_ex_size, (double)len,
+ (double)(len - fsp->fsp_name->st.st_ex_size)));
contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_FILL_SPARSE);
flush_write_cache(fsp, SIZECHANGE_FLUSH);
- if (!sparse_buf) {
- sparse_buf = SMB_CALLOC_ARRAY(char, SPARSE_BUF_WRITE_SIZE);
- if (!sparse_buf) {
- errno = ENOMEM;
+ offset = fsp->fsp_name->st.st_ex_size;
+ num_to_write = len - fsp->fsp_name->st.st_ex_size;
+
+ /* Only do this on non-stream file handles. */
+ if (fsp->base_fsp == NULL) {
+ /* for allocation try fallocate first. This can fail on some
+ * platforms e.g. when the filesystem doesn't support it and no
+ * emulation is being done by the libc (like on AIX with JFS1). In that
+ * case we do our own emulation. fallocate implementations can
+ * return ENOTSUP or EINVAL in cases like that. */
+ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_EXTEND_SIZE,
+ offset, num_to_write);
+ if (ret == ENOSPC) {
+ errno = ENOSPC;
ret = -1;
goto out;
}
- }
-
- offset = st.st_ex_size;
- num_to_write = len - st.st_ex_size;
- total = 0;
-
- while (total < num_to_write) {
- size_t curr_write_size = MIN(SPARSE_BUF_WRITE_SIZE, (num_to_write - total));
-
- pwrite_ret = SMB_VFS_PWRITE(fsp, sparse_buf, curr_write_size, offset + total);
- if (pwrite_ret == -1) {
- DEBUG(10,("vfs_fill_sparse: SMB_VFS_PWRITE for file "
- "%s failed with error %s\n",
- fsp_str_dbg(fsp), strerror(errno)));
- ret = -1;
- goto out;
- }
- if (pwrite_ret == 0) {
- ret = 0;
+ if (ret == 0) {
goto out;
}
-
- total += pwrite_ret;
+ DEBUG(10,("vfs_fill_sparse: SMB_VFS_FALLOCATE failed with "
+ "error %d. Falling back to slow manual allocation\n", ret));
}
- set_filelen_write_cache(fsp, len);
+ ret = vfs_slow_fallocate(fsp, offset, num_to_write);
+ if (ret != 0) {
+ errno = ret;
+ ret = -1;
+ }
- ret = 0;
out:
+
+ if (ret == 0) {
+ set_filelen_write_cache(fsp, len);
+ }
+
contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_FILL_SPARSE);
return ret;
}
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
- DEBUG(3,("check_reduce_named: "
+ DEBUG(3,("check_reduce_name: "
"couldn't get realpath for "
"%s (%s)\n",
fname,
DEBUG(2, ("check_reduced_name: Bad access "
"attempt: %s is a symlink outside the "
"share path\n", fname));
+ DEBUGADD(2, ("conn_rootdir =%s\n", conn_rootdir));
+ DEBUGADD(2, ("resolved_name=%s\n", resolved_name));
SAFE_FREE(resolved_name);
return NT_STATUS_ACCESS_DENIED;
}
{
int ret;
- if(fsp->is_directory || fsp->fh->fd == -1) {
+ if(fsp->fh->fd == -1) {
if (fsp->posix_open) {
ret = SMB_VFS_LSTAT(fsp->conn, fsp->fsp_name);
} else {
return handle->fns->opendir(handle, fname, mask, attributes);
}
+SMB_STRUCT_DIR *smb_vfs_call_fdopendir(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ const char *mask,
+ uint32 attributes)
+{
+ VFS_FIND(fdopendir);
+ return handle->fns->fdopendir(handle, fsp, mask, attributes);
+}
+
SMB_STRUCT_DIRENT *smb_vfs_call_readdir(struct vfs_handle_struct *handle,
SMB_STRUCT_DIR *dirp,
SMB_STRUCT_STAT *sbuf)
return handle->fns->lchown(handle, path, uid, gid);
}
+NTSTATUS vfs_chown_fsp(files_struct *fsp, uid_t uid, gid_t gid)
+{
+ int ret;
+
+ if (fsp->fh->fd != -1) {
+ /* Try fchown. */
+ ret = SMB_VFS_FCHOWN(fsp, uid, gid);
+ if (ret == 0) {
+ return NT_STATUS_OK;
+ }
+ if (ret == -1 && errno != ENOSYS) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (fsp->posix_open) {
+ ret = SMB_VFS_LCHOWN(fsp->conn,
+ fsp->fsp_name->base_name,
+ uid, gid);
+ } else {
+ ret = SMB_VFS_CHOWN(fsp->conn,
+ fsp->fsp_name->base_name,
+ uid, gid);
+ }
+ if (ret == 0) {
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+}
+
int smb_vfs_call_chdir(struct vfs_handle_struct *handle, const char *path)
{
VFS_FIND(chdir);
return handle->fns->ftruncate(handle, fsp, offset);
}
+int smb_vfs_call_fallocate(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ enum vfs_fallocate_mode mode,
+ SMB_OFF_T offset,
+ SMB_OFF_T len)
+{
+ VFS_FIND(fallocate);
+ return handle->fns->fallocate(handle, fsp, mode, offset, len);
+}
+
int smb_vfs_call_kernel_flock(struct vfs_handle_struct *handle,
struct files_struct *fsp, uint32 share_mode,
uint32_t access_mask)
}
bool smb_vfs_call_is_offline(struct vfs_handle_struct *handle,
- const char *path, SMB_STRUCT_STAT *sbuf)
+ const struct smb_filename *fname,
+ SMB_STRUCT_STAT *sbuf)
{
VFS_FIND(is_offline);
- return handle->fns->is_offline(handle, path, sbuf);
+ return handle->fns->is_offline(handle, fname, sbuf);
}
int smb_vfs_call_set_offline(struct vfs_handle_struct *handle,
- const char *path)
+ const struct smb_filename *fname)
{
VFS_FIND(set_offline);
- return handle->fns->set_offline(handle, path);
+ return handle->fns->set_offline(handle, fname);
}