static uint32_t vfswrap_fs_capabilities(struct vfs_handle_struct *handle,
enum timestamp_set_resolution *p_ts_res)
{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
connection_struct *conn = handle->conn;
uint32_t caps = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES;
struct smb_filename *smb_fname_cpath = NULL;
struct vfs_statvfs_struct statbuf;
int ret;
- smb_fname_cpath = synthetic_smb_fname(talloc_tos(), conn->connectpath,
- NULL, NULL, 0);
+ smb_fname_cpath = synthetic_smb_fname(talloc_tos(),
+ conn->connectpath,
+ NULL,
+ NULL,
+ 0,
+ 0);
if (smb_fname_cpath == NULL) {
return caps;
}
"resolution of %s "
"available on share %s, directory %s\n",
*p_ts_res == TIMESTAMP_SET_MSEC ? "msec" : "sec",
- lp_servicename(talloc_tos(), conn->params->service),
+ lp_servicename(talloc_tos(), lp_sub, conn->params->service),
conn->connectpath ));
}
TALLOC_FREE(smb_fname_cpath);
char *pathnamep = NULL;
char *local_dfs_path = NULL;
NTSTATUS status;
- int i;
+ size_t i;
uint16_t max_referral_level = r->in.req.max_referral_level;
if (DEBUGLVL(10)) {
}
/* The following call can change cwd. */
- status = get_referred_path(r, pathnamep,
+ status = get_referred_path(r,
+ handle->conn->session_info,
+ pathnamep,
handle->conn->sconn->remote_address,
handle->conn->sconn->local_address,
!handle->conn->sconn->using_smb2,
return NT_STATUS_OK;
}
+static NTSTATUS vfswrap_create_dfs_pathat(struct vfs_handle_struct *handle,
+ struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname,
+ const struct referral *reflist,
+ size_t referral_count)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ int ret;
+ char *msdfs_link = NULL;
+
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ /* Form the msdfs_link contents */
+ msdfs_link = msdfs_link_string(frame,
+ reflist,
+ referral_count);
+ if (msdfs_link == NULL) {
+ goto out;
+ }
+
+ ret = symlinkat(msdfs_link,
+ dirfsp->fh->fd,
+ smb_fname->base_name);
+ if (ret == 0) {
+ status = NT_STATUS_OK;
+ } else {
+ status = map_nt_error_from_unix(errno);
+ }
+
+ out:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*
+ * Read and return the contents of a DFS redirect given a
+ * pathname. A caller can pass in NULL for ppreflist and
+ * preferral_count but still determine if this was a
+ * DFS redirect point by getting NT_STATUS_OK back
+ * without incurring the overhead of reading and parsing
+ * the referral contents.
+ */
+
+static NTSTATUS vfswrap_read_dfs_pathat(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname,
+ struct referral **ppreflist,
+ size_t *preferral_count)
+{
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ size_t bufsize;
+ char *link_target = NULL;
+ int referral_len;
+ bool ok;
+#if defined(HAVE_BROKEN_READLINK)
+ char link_target_buf[PATH_MAX];
+#else
+ char link_target_buf[7];
+#endif
+
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ if (ppreflist == NULL && preferral_count == NULL) {
+ /*
+ * We're only checking if this is a DFS
+ * redirect. We don't need to return data.
+ */
+ bufsize = sizeof(link_target_buf);
+ link_target = link_target_buf;
+ } else {
+ bufsize = PATH_MAX;
+ link_target = talloc_array(mem_ctx, char, bufsize);
+ if (!link_target) {
+ goto err;
+ }
+ }
+
+ referral_len = readlinkat(dirfsp->fh->fd,
+ smb_fname->base_name,
+ link_target,
+ bufsize - 1);
+ if (referral_len == -1) {
+ if (errno == EINVAL) {
+ /*
+ * If the path isn't a link, readlinkat
+ * returns EINVAL. Allow the caller to
+ * detect this.
+ */
+ DBG_INFO("%s is not a link.\n", smb_fname->base_name);
+ status = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ } else {
+ status = map_nt_error_from_unix(errno);
+ DBG_ERR("Error reading "
+ "msdfs link %s: %s\n",
+ smb_fname->base_name,
+ strerror(errno));
+ }
+ goto err;
+ }
+ link_target[referral_len] = '\0';
+
+ DBG_INFO("%s -> %s\n",
+ smb_fname->base_name,
+ link_target);
+
+ if (!strnequal(link_target, "msdfs:", 6)) {
+ status = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto err;
+ }
+
+ if (ppreflist == NULL && preferral_count == NULL) {
+ /* Early return for checking if this is a DFS link. */
+ return NT_STATUS_OK;
+ }
+
+ ok = parse_msdfs_symlink(mem_ctx,
+ lp_msdfs_shuffle_referrals(SNUM(handle->conn)),
+ link_target,
+ ppreflist,
+ preferral_count);
+
+ if (ok) {
+ status = NT_STATUS_OK;
+ } else {
+ status = NT_STATUS_NO_MEMORY;
+ }
+
+ err:
+
+ if (link_target != link_target_buf) {
+ TALLOC_FREE(link_target);
+ }
+ return status;
+}
+
static NTSTATUS vfswrap_snap_check_path(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
const char *service_path,
/* Directory operations */
-static DIR *vfswrap_opendir(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- const char *mask,
- uint32_t attr)
-{
- DIR *result;
-
- START_PROFILE(syscall_opendir);
- result = opendir(smb_fname->base_name);
- END_PROFILE(syscall_opendir);
- return result;
-}
-
static DIR *vfswrap_fdopendir(vfs_handle_struct *handle,
files_struct *fsp,
const char *mask,
END_PROFILE(syscall_rewinddir);
}
-static int vfswrap_mkdir(vfs_handle_struct *handle,
+static int vfswrap_mkdirat(vfs_handle_struct *handle,
+ struct files_struct *dirfsp,
const struct smb_filename *smb_fname,
mode_t mode)
{
int result;
- const char *path = smb_fname->base_name;
- char *parent = NULL;
+ struct smb_filename *parent = NULL;
+ bool ok;
+
+ START_PROFILE(syscall_mkdirat);
- START_PROFILE(syscall_mkdir);
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
- if (lp_inherit_acls(SNUM(handle->conn))
- && parent_dirname(talloc_tos(), path, &parent, NULL)
- && directory_has_default_acl(handle->conn, parent)) {
- mode = (0777 & lp_directory_mask(SNUM(handle->conn)));
+ if (lp_inherit_acls(SNUM(handle->conn))) {
+ ok = parent_smb_fname(talloc_tos(), smb_fname, &parent, NULL);
+ if (ok && directory_has_default_acl(handle->conn,
+ dirfsp,
+ parent))
+ {
+ mode = (0777 & lp_directory_mask(SNUM(handle->conn)));
+ }
}
TALLOC_FREE(parent);
- result = mkdir(path, mode);
-
- END_PROFILE(syscall_mkdir);
- return result;
-}
-
-static int vfswrap_rmdir(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname)
-{
- int result;
+ result = mkdirat(dirfsp->fh->fd, smb_fname->base_name, mode);
- START_PROFILE(syscall_rmdir);
- result = rmdir(smb_fname->base_name);
- END_PROFILE(syscall_rmdir);
+ END_PROFILE(syscall_mkdirat);
return result;
}
START_PROFILE(syscall_open);
- if (smb_fname->stream_name) {
+ if (is_named_stream(smb_fname)) {
errno = ENOENT;
goto out;
}
uint32_t create_options,
uint32_t file_attributes,
uint32_t oplock_request,
- struct smb2_lease *lease,
+ const struct smb2_lease *lease,
uint64_t allocation_size,
uint32_t private_flags,
struct security_descriptor *sd,
SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
subreq = pthreadpool_tevent_job_send(
- state, ev, handle->conn->sconn->raw_thread_pool,
+ state, ev, handle->conn->sconn->pool,
vfs_pread_do, state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
TALLOC_FREE(subreq);
SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes);
talloc_set_destructor(state, NULL);
- if (tevent_req_error(req, ret)) {
- return;
+ if (ret != 0) {
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ /*
+ * If we get EAGAIN from pthreadpool_tevent_job_recv() this
+ * means the lower level pthreadpool failed to create a new
+ * thread. Fallback to sync processing in that case to allow
+ * some progress for the client.
+ */
+ vfs_pread_do(state);
}
tevent_req_done(req);
SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
subreq = pthreadpool_tevent_job_send(
- state, ev, handle->conn->sconn->raw_thread_pool,
+ state, ev, handle->conn->sconn->pool,
vfs_pwrite_do, state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
TALLOC_FREE(subreq);
SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes);
talloc_set_destructor(state, NULL);
- if (tevent_req_error(req, ret)) {
- return;
+ if (ret != 0) {
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ /*
+ * If we get EAGAIN from pthreadpool_tevent_job_recv() this
+ * means the lower level pthreadpool failed to create a new
+ * thread. Fallback to sync processing in that case to allow
+ * some progress for the client.
+ */
+ vfs_pwrite_do(state);
}
tevent_req_done(req);
SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
subreq = pthreadpool_tevent_job_send(
- state, ev, handle->conn->sconn->raw_thread_pool,
- vfs_fsync_do, state);
+ state, ev, handle->conn->sconn->pool, vfs_fsync_do, state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
TALLOC_FREE(subreq);
SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes);
talloc_set_destructor(state, NULL);
- if (tevent_req_error(req, ret)) {
- return;
+ if (ret != 0) {
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ /*
+ * If we get EAGAIN from pthreadpool_tevent_job_recv() this
+ * means the lower level pthreadpool failed to create a new
+ * thread. Fallback to sync processing in that case to allow
+ * some progress for the client.
+ */
+ vfs_fsync_do(state);
}
tevent_req_done(req);
START_PROFILE(syscall_lseek);
- /* Cope with 'stat' file opens. */
- if (fsp->fh->fd != -1)
- result = lseek(fsp->fh->fd, offset, whence);
-
+ result = lseek(fsp->fh->fd, offset, whence);
/*
* We want to maintain the fiction that we can seek
* on a fifo for file system purposes. This allows
return result;
}
-static int vfswrap_rename(vfs_handle_struct *handle,
+static int vfswrap_renameat(vfs_handle_struct *handle,
+ files_struct *srcfsp,
const struct smb_filename *smb_fname_src,
+ files_struct *dstfsp,
const struct smb_filename *smb_fname_dst)
{
int result = -1;
- START_PROFILE(syscall_rename);
+ START_PROFILE(syscall_renameat);
- if (smb_fname_src->stream_name || smb_fname_dst->stream_name) {
+ if (is_named_stream(smb_fname_src) || is_named_stream(smb_fname_dst)) {
errno = ENOENT;
goto out;
}
- result = rename(smb_fname_src->base_name, smb_fname_dst->base_name);
+ result = renameat(srcfsp->fh->fd,
+ smb_fname_src->base_name,
+ dstfsp->fh->fd,
+ smb_fname_dst->base_name);
out:
- END_PROFILE(syscall_rename);
+ END_PROFILE(syscall_renameat);
return result;
}
START_PROFILE(syscall_stat);
- if (smb_fname->stream_name) {
+ if (is_named_stream(smb_fname)) {
errno = ENOENT;
goto out;
}
START_PROFILE(syscall_lstat);
- if (smb_fname->stream_name) {
+ if (is_named_stream(smb_fname)) {
errno = ENOENT;
goto out;
}
*
* but I have to check that --metze
*/
+ ssize_t ret;
struct dom_sid sid;
struct dom_sid_buf buf;
uid_t uid;
/* unknown 4 bytes: this is not the length of the sid :-( */
/*unknown = IVAL(pdata,0);*/
- if (!sid_parse(_in_data + 4, sid_len, &sid)) {
+ ret = sid_parse(_in_data + 4, sid_len, &sid);
+ if (ret == -1) {
return NT_STATUS_INVALID_PARAMETER;
}
DEBUGADD(10, ("for SID: %s\n",
return NT_STATUS_NOT_SUPPORTED;
}
-static bool vfswrap_is_offline(struct vfs_handle_struct *handle,
- const struct smb_filename *fname,
- SMB_STRUCT_STAT *sbuf);
+static bool vfswrap_is_offline(struct connection_struct *conn,
+ const struct smb_filename *fname);
static NTSTATUS vfswrap_get_dos_attributes(struct vfs_handle_struct *handle,
struct smb_filename *smb_fname,
{
bool offline;
- offline = vfswrap_is_offline(handle, smb_fname, &smb_fname->st);
+ offline = vfswrap_is_offline(handle->conn, smb_fname);
if (offline) {
*dosmode |= FILE_ATTRIBUTE_OFFLINE;
}
struct vfs_aio_state aio_state;
connection_struct *conn;
TALLOC_CTX *mem_ctx;
- const struct smb_vfs_ev_glue *evg;
+ struct tevent_context *ev;
files_struct *dir_fsp;
struct smb_filename *smb_fname;
uint32_t dosmode;
static struct tevent_req *vfswrap_get_dos_attributes_send(
TALLOC_CTX *mem_ctx,
- const struct smb_vfs_ev_glue *evg,
+ struct tevent_context *ev,
struct vfs_handle_struct *handle,
files_struct *dir_fsp,
struct smb_filename *smb_fname)
{
- struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
struct tevent_req *req = NULL;
struct tevent_req *subreq = NULL;
struct vfswrap_get_dos_attributes_state *state = NULL;
*state = (struct vfswrap_get_dos_attributes_state) {
.conn = dir_fsp->conn,
.mem_ctx = mem_ctx,
- .evg = evg,
+ .ev = ev,
.dir_fsp = dir_fsp,
.smb_fname = smb_fname,
};
subreq = SMB_VFS_GETXATTRAT_SEND(state,
- evg,
+ ev,
dir_fsp,
smb_fname,
SAMBA_XATTR_DOS_ATTRIB,
struct vfswrap_get_dos_attributes_state);
ssize_t xattr_size;
DATA_BLOB blob = {0};
+ char *path = NULL;
+ char *tofree = NULL;
+ char pathbuf[PATH_MAX+1];
+ ssize_t pathlen;
+ struct smb_filename smb_fname;
+ bool offline;
NTSTATUS status;
xattr_size = SMB_VFS_GETXATTRAT_RECV(subreq,
&blob.data);
TALLOC_FREE(subreq);
if (xattr_size == -1) {
- const struct smb_vfs_ev_glue *root_evg = NULL;
-
status = map_nt_error_from_unix(state->aio_state.error);
if (state->as_root) {
}
state->as_root = true;
- root_evg = smb_vfs_ev_glue_get_root_glue(state->evg);
+ become_root();
subreq = SMB_VFS_GETXATTRAT_SEND(state,
- root_evg,
+ state->ev,
state->dir_fsp,
state->smb_fname,
SAMBA_XATTR_DOS_ATTRIB,
sizeof(fstring));
+ unbecome_root();
if (tevent_req_nomem(subreq, req)) {
return;
}
return;
}
+ pathlen = full_path_tos(state->dir_fsp->fsp_name->base_name,
+ state->smb_fname->base_name,
+ pathbuf,
+ sizeof(pathbuf),
+ &path,
+ &tofree);
+ if (pathlen == -1) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ smb_fname = (struct smb_filename) {
+ .base_name = path,
+ .st = state->smb_fname->st,
+ .flags = state->smb_fname->flags,
+ };
+
+ offline = vfswrap_is_offline(state->conn, &smb_fname);
+ if (offline) {
+ state->dosmode |= FILE_ATTRIBUTE_OFFLINE;
+ }
+ TALLOC_FREE(tofree);
+
tevent_req_done(req);
return;
}
{
bool offline;
- offline = vfswrap_is_offline(handle, fsp->fsp_name, &fsp->fsp_name->st);
+ offline = vfswrap_is_offline(handle->conn, fsp->fsp_name);
if (offline) {
*dosmode |= FILE_ATTRIBUTE_OFFLINE;
}
return;
}
- ok = change_to_user_by_fsp(state->dst_fsp);
+ ok = change_to_user_and_service_by_fsp(state->dst_fsp);
SMB_ASSERT(ok);
state->dst_fsp = NULL;
}
{
struct tevent_req *req;
struct vfswrap_offload_write_state *state = NULL;
+ /* off_t is signed! */
+ off_t max_offset = INT64_MAX - to_copy;
size_t num = MIN(to_copy, COPYCHUNK_MAX_TOTAL_LEN);
files_struct *src_fsp = NULL;
NTSTATUS status;
return tevent_req_post(req, ev);
}
+ if (state->src_off > max_offset) {
+ /*
+ * Protect integer checks below.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (state->src_off < 0) {
+ /*
+ * Protect integer checks below.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (state->dst_off > max_offset) {
+ /*
+ * Protect integer checks below.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (state->dst_off < 0) {
+ /*
+ * Protect integer checks below.
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
status = vfs_offload_token_db_fetch_fsp(vfswrap_offload_ctx,
token, &src_fsp);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
- ok = change_to_user_by_fsp(src_fsp);
+ ok = change_to_user_and_service_by_fsp(src_fsp);
if (!ok) {
tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return tevent_req_post(req, ev);
}
- state->src_ev = src_fsp->conn->user_ev_ctx;
+ state->src_ev = src_fsp->conn->sconn->ev_ctx;
state->src_fsp = src_fsp;
- state->buf = talloc_array(state, uint8_t, num);
- if (tevent_req_nomem(state->buf, req)) {
- return tevent_req_post(req, ev);
- }
-
status = vfs_stat_fsp(src_fsp);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
- if (src_fsp->fsp_name->st.st_ex_size < state->src_off + num) {
+ if (src_fsp->fsp_name->st.st_ex_size < state->src_off + to_copy) {
/*
* [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
* If the SourceOffset or SourceOffset + Length extends beyond
return tevent_req_post(req, ev);
}
+ state->buf = talloc_array(state, uint8_t, num);
+ if (tevent_req_nomem(state->buf, req)) {
+ return tevent_req_post(req, ev);
+ }
+
status = vfswrap_offload_write_loop(req);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
nread = SMB_VFS_PREAD_RECV(subreq, &aio_state);
TALLOC_FREE(subreq);
if (nread == -1) {
- DBG_ERR("read failed: %s\n", strerror(errno));
+ DBG_ERR("read failed: %s\n", strerror(aio_state.error));
tevent_req_nterror(req, map_nt_error_from_unix(aio_state.error));
return;
}
state->src_off += nread;
- ok = change_to_user_by_fsp(state->dst_fsp);
+ ok = change_to_user_and_service_by_fsp(state->dst_fsp);
if (!ok) {
tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
return;
nwritten = SMB_VFS_PWRITE_RECV(subreq, &aio_state);
TALLOC_FREE(subreq);
if (nwritten == -1) {
- DBG_ERR("write failed: %s\n", strerror(errno));
+ DBG_ERR("write failed: %s\n", strerror(aio_state.error));
tevent_req_nterror(req, map_nt_error_from_unix(aio_state.error));
return;
}
return;
}
- ok = change_to_user_by_fsp(state->src_fsp);
+ ok = change_to_user_and_service_by_fsp(state->src_fsp);
if (!ok) {
tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
return;
return result;
}
-static int vfswrap_unlink(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname)
+static int vfswrap_unlinkat(vfs_handle_struct *handle,
+ struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname,
+ int flags)
{
int result = -1;
- START_PROFILE(syscall_unlink);
+ START_PROFILE(syscall_unlinkat);
- if (smb_fname->stream_name) {
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ if (is_named_stream(smb_fname)) {
errno = ENOENT;
goto out;
}
- result = unlink(smb_fname->base_name);
+ result = unlinkat(dirfsp->fh->fd,
+ smb_fname->base_name,
+ flags);
out:
- END_PROFILE(syscall_unlink);
+ END_PROFILE(syscall_unlinkat);
return result;
}
return result;
}
-static int vfswrap_chown(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- uid_t uid,
- gid_t gid)
-{
- int result;
-
- START_PROFILE(syscall_chown);
- result = chown(smb_fname->base_name, uid, gid);
- END_PROFILE(syscall_chown);
- return result;
-}
-
static int vfswrap_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
{
#ifdef HAVE_FCHOWN
result,
NULL,
NULL,
+ 0,
0);
/*
* sys_getwd() *always* returns malloced memory.
START_PROFILE(syscall_ntimes);
- if (smb_fname->stream_name) {
+ if (is_named_stream(smb_fname)) {
errno = ENOENT;
goto out;
}
if (ft != NULL) {
- if (null_timespec(ft->atime)) {
+ if (is_omit_timespec(&ft->atime)) {
ft->atime= smb_fname->st.st_ex_atime;
}
- if (null_timespec(ft->mtime)) {
+ if (is_omit_timespec(&ft->mtime)) {
ft->mtime = smb_fname->st.st_ex_mtime;
}
- if (!null_timespec(ft->create_time)) {
+ if (!is_omit_timespec(&ft->create_time)) {
set_create_timespec_ea(handle->conn,
smb_fname,
ft->create_time);
START_PROFILE(syscall_ftruncate);
- if (lp_strict_allocate(SNUM(fsp->conn)) && !fsp->is_sparse) {
+ if (lp_strict_allocate(SNUM(fsp->conn)) && !fsp->fsp_flags.is_sparse) {
result = strict_allocate_ftruncate(handle, fsp, len);
END_PROFILE(syscall_ftruncate);
return result;
START_PROFILE(syscall_fcntl_lock);
- if (fsp->use_ofd_locks || !lp_parm_bool(SNUM(fsp->conn),
- "smbd",
- "force process locks",
- false)) {
- op = map_process_lock_to_ofd_lock(op, &fsp->use_ofd_locks);
+ if (fsp->fsp_flags.use_ofd_locks) {
+ op = map_process_lock_to_ofd_lock(op);
}
result = fcntl_lock(fsp->fh->fd, op, offset, count, type);
}
static int vfswrap_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
- uint32_t share_mode, uint32_t access_mask)
+ uint32_t share_access, uint32_t access_mask)
{
START_PROFILE(syscall_kernel_flock);
- kernel_flock(fsp->fh->fd, share_mode, access_mask);
+ kernel_flock(fsp->fh->fd, share_access, access_mask);
END_PROFILE(syscall_kernel_flock);
return 0;
}
+static int vfswrap_fcntl(vfs_handle_struct *handle, files_struct *fsp, int cmd,
+ va_list cmd_arg)
+{
+ void *argp;
+ va_list dup_cmd_arg;
+ int result;
+ int val;
+
+ START_PROFILE(syscall_fcntl);
+
+ va_copy(dup_cmd_arg, cmd_arg);
+
+ switch(cmd) {
+ case F_SETLK:
+ case F_SETLKW:
+ case F_GETLK:
+#if defined(HAVE_OFD_LOCKS)
+ case F_OFD_SETLK:
+ case F_OFD_SETLKW:
+ case F_OFD_GETLK:
+#endif
+#if defined(HAVE_F_OWNER_EX)
+ case F_GETOWN_EX:
+ case F_SETOWN_EX:
+#endif
+#if defined(HAVE_RW_HINTS)
+ case F_GET_RW_HINT:
+ case F_SET_RW_HINT:
+ case F_GET_FILE_RW_HINT:
+ case F_SET_FILE_RW_HINT:
+#endif
+ argp = va_arg(dup_cmd_arg, void *);
+ result = sys_fcntl_ptr(fsp->fh->fd, cmd, argp);
+ break;
+ default:
+ val = va_arg(dup_cmd_arg, int);
+ result = sys_fcntl_int(fsp->fh->fd, cmd, val);
+ }
+
+ va_end(dup_cmd_arg);
+
+ END_PROFILE(syscall_fcntl);
+ return result;
+}
+
static bool vfswrap_getlock(vfs_handle_struct *handle, files_struct *fsp, off_t *poffset, off_t *pcount, int *ptype, pid_t *ppid)
{
bool result;
START_PROFILE(syscall_fcntl_getlock);
- if (fsp->use_ofd_locks || !lp_parm_bool(SNUM(fsp->conn),
- "smbd",
- "force process locks",
- false)) {
- op = map_process_lock_to_ofd_lock(op, &fsp->use_ofd_locks);
+ if (fsp->fsp_flags.use_ofd_locks) {
+ op = map_process_lock_to_ofd_lock(op);
}
result = fcntl_getlock(fsp->fh->fd, op, poffset, pcount, ptype, ppid);
return result;
}
-static int vfswrap_symlink(vfs_handle_struct *handle,
+static int vfswrap_symlinkat(vfs_handle_struct *handle,
const char *link_target,
+ struct files_struct *dirfsp,
const struct smb_filename *new_smb_fname)
{
int result;
- START_PROFILE(syscall_symlink);
- result = symlink(link_target, new_smb_fname->base_name);
- END_PROFILE(syscall_symlink);
+ START_PROFILE(syscall_symlinkat);
+
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ result = symlinkat(link_target,
+ dirfsp->fh->fd,
+ new_smb_fname->base_name);
+ END_PROFILE(syscall_symlinkat);
return result;
}
-static int vfswrap_readlink(vfs_handle_struct *handle,
+static int vfswrap_readlinkat(vfs_handle_struct *handle,
+ files_struct *dirfsp,
const struct smb_filename *smb_fname,
char *buf,
size_t bufsiz)
{
int result;
- START_PROFILE(syscall_readlink);
- result = readlink(smb_fname->base_name, buf, bufsiz);
- END_PROFILE(syscall_readlink);
+ START_PROFILE(syscall_readlinkat);
+
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ result = readlinkat(dirfsp->fh->fd,
+ smb_fname->base_name,
+ buf,
+ bufsiz);
+
+ END_PROFILE(syscall_readlinkat);
return result;
}
-static int vfswrap_link(vfs_handle_struct *handle,
+static int vfswrap_linkat(vfs_handle_struct *handle,
+ files_struct *srcfsp,
const struct smb_filename *old_smb_fname,
- const struct smb_filename *new_smb_fname)
+ files_struct *dstfsp,
+ const struct smb_filename *new_smb_fname,
+ int flags)
{
int result;
- START_PROFILE(syscall_link);
- result = link(old_smb_fname->base_name, new_smb_fname->base_name);
- END_PROFILE(syscall_link);
+ START_PROFILE(syscall_linkat);
+
+ SMB_ASSERT(srcfsp == srcfsp->conn->cwd_fsp);
+ SMB_ASSERT(dstfsp == dstfsp->conn->cwd_fsp);
+
+ result = linkat(srcfsp->fh->fd,
+ old_smb_fname->base_name,
+ dstfsp->fh->fd,
+ new_smb_fname->base_name,
+ flags);
+
+ END_PROFILE(syscall_linkat);
return result;
}
-static int vfswrap_mknod(vfs_handle_struct *handle,
+static int vfswrap_mknodat(vfs_handle_struct *handle,
+ files_struct *dirfsp,
const struct smb_filename *smb_fname,
mode_t mode,
SMB_DEV_T dev)
{
int result;
- START_PROFILE(syscall_mknod);
- result = sys_mknod(smb_fname->base_name, mode, dev);
- END_PROFILE(syscall_mknod);
+ START_PROFILE(syscall_mknodat);
+
+ SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
+
+ result = sys_mknodat(dirfsp->fh->fd,
+ smb_fname->base_name,
+ mode,
+ dev);
+
+ END_PROFILE(syscall_mknodat);
return result;
}
result = sys_realpath(smb_fname->base_name);
END_PROFILE(syscall_realpath);
if (result) {
- result_fname = synthetic_smb_fname(ctx, result, NULL, NULL, 0);
+ result_fname = synthetic_smb_fname(ctx,
+ result,
+ NULL,
+ NULL,
+ 0,
+ 0);
SAFE_FREE(result);
}
return result_fname;
return key;
}
+static uint64_t vfswrap_fs_file_id(struct vfs_handle_struct *handle,
+ const SMB_STRUCT_STAT *psbuf)
+{
+ uint64_t file_id;
+
+ if (!(psbuf->st_ex_iflags & ST_EX_IFLAG_CALCULATED_FILE_ID)) {
+ return psbuf->st_ex_file_id;
+ }
+
+ if (handle->conn->base_share_dev == psbuf->st_ex_dev) {
+ return (uint64_t)psbuf->st_ex_ino;
+ }
+
+ /* FileIDLow */
+ file_id = ((psbuf->st_ex_ino) & UINT32_MAX);
+
+ /* FileIDHigh */
+ file_id |= ((uint64_t)((psbuf->st_ex_dev) & UINT32_MAX)) << 32;
+
+ return file_id;
+}
+
static NTSTATUS vfswrap_streaminfo(vfs_handle_struct *handle,
struct files_struct *fsp,
const struct smb_filename *smb_fname,
struct stream_struct *tmp_streams = NULL;
int ret;
- if ((fsp != NULL) && (fsp->is_directory)) {
+ if ((fsp != NULL) && (fsp->fsp_flags.is_directory)) {
/*
* No default streams on directories
*/
static NTSTATUS vfswrap_brl_lock_windows(struct vfs_handle_struct *handle,
struct byte_range_lock *br_lck,
- struct lock_struct *plock,
- bool blocking_lock)
+ struct lock_struct *plock)
{
SMB_ASSERT(plock->lock_flav == WINDOWS_LOCK);
/* Note: blr is not used in the default implementation. */
- return brl_lock_windows_default(br_lck, plock, blocking_lock);
+ return brl_lock_windows_default(br_lck, plock);
}
static bool vfswrap_brl_unlock_windows(struct vfs_handle_struct *handle,
- struct messaging_context *msg_ctx,
struct byte_range_lock *br_lck,
const struct lock_struct *plock)
{
SMB_ASSERT(plock->lock_flav == WINDOWS_LOCK);
- return brl_unlock_windows_default(msg_ctx, br_lck, plock);
-}
-
-static bool vfswrap_brl_cancel_windows(struct vfs_handle_struct *handle,
- struct byte_range_lock *br_lck,
- struct lock_struct *plock)
-{
- SMB_ASSERT(plock->lock_flav == WINDOWS_LOCK);
-
- /* Note: blr is not used in the default implementation. */
- return brl_lock_cancel_default(br_lck, plock);
+ return brl_unlock_windows_default(br_lck, plock);
}
static bool vfswrap_strict_lock_check(struct vfs_handle_struct *handle,
}
struct vfswrap_getxattrat_state {
- int dirfd;
+ struct tevent_context *ev;
+ files_struct *dir_fsp;
+ const struct smb_filename *smb_fname;
+ struct tevent_req *req;
+
+ /*
+ * The following variables are talloced off "state" which is protected
+ * by a destructor and thus are guaranteed to be safe to be used in the
+ * job function in the worker thread.
+ */
char *name;
- size_t xattr_bufsize;
const char *xattr_name;
- ssize_t xattr_size;
uint8_t *xattr_value;
+ struct security_unix_token *token;
+ ssize_t xattr_size;
struct vfs_aio_state vfs_aio_state;
SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes);
};
return -1;
}
-static void vfswrap_getxattrat_do(void *private_data);
+static void vfswrap_getxattrat_do_sync(struct tevent_req *req);
+static void vfswrap_getxattrat_do_async(void *private_data);
static void vfswrap_getxattrat_done(struct tevent_req *subreq);
static struct tevent_req *vfswrap_getxattrat_send(
TALLOC_CTX *mem_ctx,
- const struct smb_vfs_ev_glue *evg,
+ struct tevent_context *ev,
struct vfs_handle_struct *handle,
files_struct *dir_fsp,
const struct smb_filename *smb_fname,
const char *xattr_name,
size_t alloc_hint)
{
- struct tevent_context *ev = smb_vfs_ev_glue_ev_ctx(evg);
- struct pthreadpool_tevent *tp = smb_vfs_ev_glue_tp_chdir_safe(evg);
struct tevent_req *req = NULL;
struct tevent_req *subreq = NULL;
struct vfswrap_getxattrat_state *state = NULL;
+ size_t max_threads = 0;
+ bool have_per_thread_cwd = false;
+ bool have_per_thread_creds = false;
+ bool do_async = false;
req = tevent_req_create(mem_ctx, &state,
struct vfswrap_getxattrat_state);
return NULL;
}
*state = (struct vfswrap_getxattrat_state) {
- .dirfd = dir_fsp->fh->fd,
- .xattr_bufsize = alloc_hint,
+ .ev = ev,
+ .dir_fsp = dir_fsp,
+ .smb_fname = smb_fname,
+ .req = req,
};
+ max_threads = pthreadpool_tevent_max_threads(dir_fsp->conn->sconn->pool);
+ if (max_threads >= 1) {
+ /*
+ * We need a non sync threadpool!
+ */
+ have_per_thread_cwd = per_thread_cwd_supported();
+ }
+#ifdef HAVE_LINUX_THREAD_CREDENTIALS
+ have_per_thread_creds = true;
+#endif
+ if (have_per_thread_cwd && have_per_thread_creds) {
+ do_async = true;
+ }
+
SMBPROFILE_BYTES_ASYNC_START(syscall_asys_getxattrat, profile_p,
state->profile_bytes, 0);
- if (state->dirfd == -1) {
+ if (dir_fsp->fh->fd == -1) {
DBG_ERR("Need a valid directory fd\n");
tevent_req_error(req, EINVAL);
return tevent_req_post(req, ev);
}
+ if (alloc_hint > 0) {
+ state->xattr_value = talloc_zero_array(state,
+ uint8_t,
+ alloc_hint);
+ if (tevent_req_nomem(state->xattr_value, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (!do_async) {
+ vfswrap_getxattrat_do_sync(req);
+ return tevent_req_post(req, ev);
+ }
+
/*
* Now allocate all parameters from a memory context that won't go away
* no matter what. These paremeters will get used in threads and we
return tevent_req_post(req, ev);
}
- if (state->xattr_bufsize > 0) {
- state->xattr_value = talloc_zero_array(state,
- uint8_t,
- state->xattr_bufsize);
- if (tevent_req_nomem(state->xattr_value, req)) {
- return tevent_req_post(req, ev);
- }
+ /*
+ * This is a hot codepath so at first glance one might think we should
+ * somehow optimize away the token allocation and do a
+ * talloc_reference() or similar black magic instead. But due to the
+ * talloc_stackframe pool per SMB2 request this should be a simple copy
+ * without a malloc in most cases.
+ */
+ if (geteuid() == sec_initial_uid()) {
+ state->token = root_unix_token(state);
+ } else {
+ state->token = copy_unix_token(
+ state,
+ dir_fsp->conn->session_info->unix_token);
+ }
+ if (tevent_req_nomem(state->token, req)) {
+ return tevent_req_post(req, ev);
}
SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->profile_bytes);
- subreq = pthreadpool_tevent_job_send(state,
- ev,
- tp,
- vfswrap_getxattrat_do,
- state);
+ subreq = pthreadpool_tevent_job_send(
+ state,
+ ev,
+ dir_fsp->conn->sconn->pool,
+ vfswrap_getxattrat_do_async,
+ state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
return req;
}
-static void vfswrap_getxattrat_do(void *private_data)
+static void vfswrap_getxattrat_do_sync(struct tevent_req *req)
+{
+ struct vfswrap_getxattrat_state *state = tevent_req_data(
+ req, struct vfswrap_getxattrat_state);
+ char *path = NULL;
+ char *tofree = NULL;
+ char pathbuf[PATH_MAX+1];
+ ssize_t pathlen;
+ int err;
+
+ pathlen = full_path_tos(state->dir_fsp->fsp_name->base_name,
+ state->smb_fname->base_name,
+ pathbuf,
+ sizeof(pathbuf),
+ &path,
+ &tofree);
+ if (pathlen == -1) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ state->xattr_size = getxattr(path,
+ state->xattr_name,
+ state->xattr_value,
+ talloc_array_length(state->xattr_value));
+ err = errno;
+ TALLOC_FREE(tofree);
+ if (state->xattr_size == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static void vfswrap_getxattrat_do_async(void *private_data)
{
struct vfswrap_getxattrat_state *state = talloc_get_type_abort(
private_data, struct vfswrap_getxattrat_state);
/*
* Here we simulate a getxattrat()
* call using fchdir();getxattr()
- *
- * We don't need to revert the directory
- * change as pthreadpool_tevent wrapper
- * handlers that.
*/
- SMB_ASSERT(pthreadpool_tevent_current_job_per_thread_cwd());
- ret = fchdir(state->dirfd);
+ per_thread_cwd_activate();
+
+ /* Become the correct credential on this thread. */
+ ret = set_thread_credentials(state->token->uid,
+ state->token->gid,
+ (size_t)state->token->ngroups,
+ state->token->groups);
+ if (ret != 0) {
+ state->xattr_size = -1;
+ state->vfs_aio_state.error = errno;
+ goto end_profile;
+ }
+
+ ret = fchdir(state->dir_fsp->fh->fd);
if (ret == -1) {
state->xattr_size = -1;
state->vfs_aio_state.error = errno;
state->xattr_size = getxattr(state->name,
state->xattr_name,
state->xattr_value,
- state->xattr_bufsize);
+ talloc_array_length(state->xattr_value));
if (state->xattr_size == -1) {
state->vfs_aio_state.error = errno;
}
struct vfswrap_getxattrat_state *state = tevent_req_data(
req, struct vfswrap_getxattrat_state);
int ret;
+ bool ok;
+
+ /*
+ * Make sure we run as the user again
+ */
+ ok = change_to_user_and_service_by_fsp(state->dir_fsp);
+ SMB_ASSERT(ok);
ret = pthreadpool_tevent_job_recv(subreq);
TALLOC_FREE(subreq);
SMBPROFILE_BYTES_ASYNC_END(state->profile_bytes);
talloc_set_destructor(state, NULL);
- if (tevent_req_error(req, ret)) {
+ if (ret != 0) {
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ /*
+ * If we get EAGAIN from pthreadpool_tevent_job_recv() this
+ * means the lower level pthreadpool failed to create a new
+ * thread. Fallback to sync processing in that case to allow
+ * some progress for the client.
+ */
+ vfswrap_getxattrat_do_sync(req);
return;
}
return false;
}
-static bool vfswrap_is_offline(struct vfs_handle_struct *handle,
- const struct smb_filename *fname,
- SMB_STRUCT_STAT *sbuf)
+static bool vfswrap_is_offline(struct connection_struct *conn,
+ const struct smb_filename *fname)
{
NTSTATUS status;
char *path;
return false;
}
- if (!lp_dmapi_support(SNUM(handle->conn)) || !dmapi_have_session()) {
+ if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
#if defined(ENOTSUP)
errno = ENOTSUP;
#endif
.statvfs_fn = vfswrap_statvfs,
.fs_capabilities_fn = vfswrap_fs_capabilities,
.get_dfs_referrals_fn = vfswrap_get_dfs_referrals,
+ .create_dfs_pathat_fn = vfswrap_create_dfs_pathat,
+ .read_dfs_pathat_fn = vfswrap_read_dfs_pathat,
.snap_check_path_fn = vfswrap_snap_check_path,
.snap_create_fn = vfswrap_snap_create,
.snap_delete_fn = vfswrap_snap_delete,
/* Directory operations */
- .opendir_fn = vfswrap_opendir,
.fdopendir_fn = vfswrap_fdopendir,
.readdir_fn = vfswrap_readdir,
.readdir_attr_fn = vfswrap_readdir_attr,
.seekdir_fn = vfswrap_seekdir,
.telldir_fn = vfswrap_telldir,
.rewind_dir_fn = vfswrap_rewinddir,
- .mkdir_fn = vfswrap_mkdir,
- .rmdir_fn = vfswrap_rmdir,
+ .mkdirat_fn = vfswrap_mkdirat,
.closedir_fn = vfswrap_closedir,
/* File operations */
.lseek_fn = vfswrap_lseek,
.sendfile_fn = vfswrap_sendfile,
.recvfile_fn = vfswrap_recvfile,
- .rename_fn = vfswrap_rename,
+ .renameat_fn = vfswrap_renameat,
.fsync_send_fn = vfswrap_fsync_send,
.fsync_recv_fn = vfswrap_fsync_recv,
.stat_fn = vfswrap_stat,
.fstat_fn = vfswrap_fstat,
.lstat_fn = vfswrap_lstat,
.get_alloc_size_fn = vfswrap_get_alloc_size,
- .unlink_fn = vfswrap_unlink,
+ .unlinkat_fn = vfswrap_unlinkat,
.chmod_fn = vfswrap_chmod,
.fchmod_fn = vfswrap_fchmod,
- .chown_fn = vfswrap_chown,
.fchown_fn = vfswrap_fchown,
.lchown_fn = vfswrap_lchown,
.chdir_fn = vfswrap_chdir,
.fallocate_fn = vfswrap_fallocate,
.lock_fn = vfswrap_lock,
.kernel_flock_fn = vfswrap_kernel_flock,
+ .fcntl_fn = vfswrap_fcntl,
.linux_setlease_fn = vfswrap_linux_setlease,
.getlock_fn = vfswrap_getlock,
- .symlink_fn = vfswrap_symlink,
- .readlink_fn = vfswrap_readlink,
- .link_fn = vfswrap_link,
- .mknod_fn = vfswrap_mknod,
+ .symlinkat_fn = vfswrap_symlinkat,
+ .readlinkat_fn = vfswrap_readlinkat,
+ .linkat_fn = vfswrap_linkat,
+ .mknodat_fn = vfswrap_mknodat,
.realpath_fn = vfswrap_realpath,
.chflags_fn = vfswrap_chflags,
.file_id_create_fn = vfswrap_file_id_create,
+ .fs_file_id_fn = vfswrap_fs_file_id,
.streaminfo_fn = vfswrap_streaminfo,
.get_real_filename_fn = vfswrap_get_real_filename,
.connectpath_fn = vfswrap_connectpath,
.brl_lock_windows_fn = vfswrap_brl_lock_windows,
.brl_unlock_windows_fn = vfswrap_brl_unlock_windows,
- .brl_cancel_windows_fn = vfswrap_brl_cancel_windows,
.strict_lock_check_fn = vfswrap_strict_lock_check,
.translate_name_fn = vfswrap_translate_name,
.fsctl_fn = vfswrap_fsctl,