static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
static struct tevent_req *skel_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
state->handle = handle;
subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
- src_fsp, src_off,
+ fsctl, token, transfer_offset,
dest_fsp, dest_off, num, flags);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
+++ /dev/null
-^samba3.smb2.ioctl.copy_chunk_across_shares
-^samba3.smb2.ioctl fs_specific.copy_chunk_across_shares
struct tevent_req *(*offload_write_send_fn)(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t to_copy,
struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
#define SMB_VFS_NEXT_OFFLOAD_READ_RECV(req, handle, mem_ctx, token_blob) \
smb_vfs_call_offload_read_recv((req), (handle)->next, (mem_ctx), (token_blob))
-#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
- smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
-#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp, src_off, dest_fsp, dest_off, num, flags) \
- smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (src_fsp), (src_off), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_OFFLOAD_WRITE_SEND(conn, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
+ smb_vfs_call_offload_write_send((conn)->vfs_handles, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
+#define SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, fsctl, token, transfer_offset, dest_fsp, dest_off, num, flags) \
+ smb_vfs_call_offload_write_send((handle)->next, (mem_ctx), (ev), (fsctl), (token), (transfer_offset), (dest_fsp), (dest_off), (num), (flags))
#define SMB_VFS_OFFLOAD_WRITE_RECV(conn, req, copied) \
smb_vfs_call_offload_write_recv((conn)->vfs_handles, (req), (copied))
#include "includes.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
+#include "../libcli/security/security.h"
#include "dbwrap/dbwrap.h"
#include "dbwrap/dbwrap_rbt.h"
#include "dbwrap/dbwrap_open.h"
return NT_STATUS_OK;
}
+
+NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
+ files_struct *src_fsp,
+ files_struct *dst_fsp)
+{
+ if (src_fsp->vuid != dst_fsp->vuid) {
+ DBG_INFO("copy chunk handles not in the same session.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!NT_STATUS_IS_OK(src_fsp->op->status)) {
+ DBG_INFO("copy chunk source handle invalid: %s\n",
+ nt_errstr(src_fsp->op->status));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!NT_STATUS_IS_OK(dst_fsp->op->status)) {
+ DBG_INFO("copy chunk destination handle invalid: %s\n",
+ nt_errstr(dst_fsp->op->status));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (src_fsp->deferred_close != NULL) {
+ DBG_INFO("copy chunk src handle with deferred close.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dst_fsp->deferred_close != NULL) {
+ DBG_INFO("copy chunk dst handle with deferred close.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (src_fsp->is_directory) {
+ DBG_INFO("copy chunk no read on src directory handle (%s).\n",
+ smb_fname_str_dbg(src_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dst_fsp->is_directory) {
+ DBG_INFO("copy chunk no read on dst directory handle (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
+ DBG_INFO("copy chunk no access on IPC$ handle.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
+ DBG_INFO("copy chunk no access on PRINT handle.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
+ * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
+ * the following are true:
+ * - The Open.GrantedAccess of the destination file does not include
+ * FILE_WRITE_DATA or FILE_APPEND_DATA.
+ *
+ * A non writable dst handle also doesn't make sense for other fsctls.
+ */
+ if (!CHECK_WRITE(dst_fsp)) {
+ DBG_INFO("dest handle not writable (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * - The Open.GrantedAccess of the destination file does not include
+ * FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
+ */
+ if ((fsctl == FSCTL_SRV_COPYCHUNK) && !CHECK_READ_IOCTL(dst_fsp)) {
+ DBG_INFO("copy chunk no read on dest handle (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * - The Open.GrantedAccess of the source file does not include
+ * FILE_READ_DATA access.
+ */
+ if (!CHECK_READ_SMB2(src_fsp)) {
+ DBG_INFO("src handle not readable (%s).\n",
+ smb_fname_str_dbg(src_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
const files_struct *fsp,
uint32_t fsctl,
DATA_BLOB *token_blob);
+NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
+ files_struct *src_fsp,
+ files_struct *dst_fsp);
#endif
static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
struct btrfs_ioctl_clone_range_args cr_args;
struct lock_struct src_lck;
struct lock_struct dest_lck;
+ off_t src_off = transfer_offset;
+ files_struct *src_fsp = NULL;
int ret;
+ bool handle_offload_write = true;
NTSTATUS status;
req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
cc_state->handle = handle;
+ status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
+ token, &src_fsp);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ switch (fsctl) {
+ case FSCTL_SRV_COPYCHUNK:
+ case FSCTL_SRV_COPYCHUNK_WRITE:
+ case FSCTL_DUP_EXTENTS_TO_FILE:
+ break;
+
+ default:
+ handle_offload_write = false;
+ break;
+ }
+
if (num == 0) {
/*
* With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
* all data from @src_offset->EOF! This is certainly not what
* the caller expects, and not what vfs_default does.
*/
+ handle_offload_write = false;
+ }
+
+ if (!handle_offload_write) {
cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
cc_state, ev,
- src_fsp,
- src_off,
+ fsctl,
+ token,
+ transfer_offset,
dest_fsp,
dest_off,
num, flags);
return req;
}
+ status = vfs_offload_token_check_handles(
+ fsctl, src_fsp, dest_fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
status = vfs_stat_fsp(src_fsp);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
(unsigned long long)cr_args.dest_offset));
cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
cc_state, ev,
- src_fsp,
- src_off,
+ fsctl,
+ token,
+ transfer_offset,
dest_fsp,
dest_off,
num, flags);
struct lock_struct read_lck;
bool write_lck_locked;
struct lock_struct write_lck;
+ DATA_BLOB *token;
struct files_struct *src_fsp;
off_t src_off;
struct files_struct *dst_fsp;
struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t to_copy,
struct tevent_req *req;
struct vfswrap_offload_write_state *state = NULL;
size_t num = MIN(to_copy, COPYCHUNK_MAX_TOTAL_LEN);
+ files_struct *src_fsp = NULL;
NTSTATUS status;
- DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
-
req = tevent_req_create(mem_ctx, &state,
struct vfswrap_offload_write_state);
if (req == NULL) {
*state = (struct vfswrap_offload_write_state) {
.ev = ev,
- .src_fsp = src_fsp,
- .src_off = src_off,
+ .token = token,
+ .src_off = transfer_offset,
.dst_fsp = dest_fsp,
.dst_off = dest_off,
.to_copy = to_copy,
.flags = flags,
};
+ switch (fsctl) {
+ case FSCTL_SRV_COPYCHUNK:
+ case FSCTL_SRV_COPYCHUNK_WRITE:
+ break;
+
+ case FSCTL_OFFLOAD_WRITE:
+ tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ return tevent_req_post(req, ev);
+
+ default:
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * From here on we assume a copy-chunk fsctl
+ */
+
if (to_copy == 0) {
tevent_req_done(req);
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);
+ }
+ state->src_fsp = src_fsp;
+
+ DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
+
+ status = vfs_offload_token_check_handles(fsctl, src_fsp, dest_fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ 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);
return tevent_req_post(req, ev);
}
- if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
+ if (src_fsp->fsp_name->st.st_ex_size < state->src_off + num) {
/*
* [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
* If the SourceOffset or SourceOffset + Length extends beyond
static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
struct fruit_offload_write_state *state;
NTSTATUS status;
struct fruit_config_data *config;
+ off_t src_off = transfer_offset;
+ files_struct *src_fsp = NULL;
off_t to_copy = num;
DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
return NULL;
}
state->handle = handle;
- state->src_fsp = src_fsp;
state->dst_fsp = dest_fsp;
+ switch (fsctl) {
+ case FSCTL_SRV_COPYCHUNK:
+ case FSCTL_SRV_COPYCHUNK_WRITE:
+ status = vfs_offload_token_db_fetch_fsp(fruit_offload_ctx,
+ token, &src_fsp);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ state->src_fsp = src_fsp;
+ break;
+ default:
+ break;
+ }
+
/*
* Check if this a OS X copyfile style copychunk request with
* a requested chunk count of 0 that was translated to a
* = dest_off = num = 0.
*/
if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
- src_fsp->aapl_copyfile_supported &&
+ src_fsp != NULL && src_fsp->aapl_copyfile_supported &&
dest_fsp->aapl_copyfile_supported)
{
status = vfs_stat_fsp(src_fsp);
subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
mem_ctx,
ev,
- src_fsp,
- src_off,
+ fsctl,
+ token,
+ transfer_offset,
dest_fsp,
dest_off,
to_copy,
static struct tevent_req *smb_full_audit_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
{
struct tevent_req *req;
- req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev, src_fsp,
- src_off, dest_fsp, dest_off, num,
+ req = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, mem_ctx, ev,
+ fsctl, token, transfer_offset,
+ dest_fsp, dest_off, num,
flags);
do_log(SMB_VFS_OP_OFFLOAD_WRITE_SEND, req, handle, "");
static struct tevent_req *smb_time_audit_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
state->handle = handle;
clock_gettime_mono(&state->ts_send);
subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle, state, ev,
- src_fsp, src_off,
+ fsctl, token, transfer_offset,
dest_fsp, dest_off, num, flags);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
struct fsctl_dup_extents_state {
struct tevent_context *ev;
struct connection_struct *conn;
- struct files_struct *src_fsp;
struct files_struct *dst_fsp;
struct fsctl_dup_extents_to_file dup_extents;
};
tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
return tevent_req_post(req, ev);
}
- state->src_fsp = src_fsp;
status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
&state->dup_extents);
subreq, struct tevent_req);
struct fsctl_dup_extents_state *state = tevent_req_data(
req, struct fsctl_dup_extents_state);
- DATA_BLOB token_blob;
+ DATA_BLOB token;
NTSTATUS status;
status = SMB_VFS_OFFLOAD_READ_RECV(subreq, state->dst_fsp->conn,
- state, &token_blob);
+ state, &token);
if (tevent_req_nterror(req, status)) {
return;
}
subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
state,
state->ev,
- state->src_fsp,
+ FSCTL_DUP_EXTENTS_TO_FILE,
+ &token,
state->dup_extents.source_off,
state->dst_fsp,
state->dup_extents.target_off,
uint32_t current_chunk;
NTSTATUS status;
off_t total_written;
+ uint32_t ctl_code;
+ DATA_BLOB token;
struct files_struct *src_fsp;
struct files_struct *dst_fsp;
enum {
};
static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
-static NTSTATUS copychunk_check_handles(uint32_t ctl_code,
- struct files_struct *src_fsp,
- struct files_struct *dst_fsp)
-{
- /*
- * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
- * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
- * the following are true:
- * - The Open.GrantedAccess of the destination file does not include
- * FILE_WRITE_DATA or FILE_APPEND_DATA.
- */
- if (!CHECK_WRITE(dst_fsp)) {
- DEBUG(5, ("copy chunk no write on dest handle (%s).\n",
- smb_fname_str_dbg(dst_fsp->fsp_name) ));
- return NT_STATUS_ACCESS_DENIED;
- }
- /*
- * - The Open.GrantedAccess of the destination file does not include
- * FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
- */
- if ((ctl_code == FSCTL_SRV_COPYCHUNK) &&
- !CHECK_READ_IOCTL(dst_fsp)) {
- DEBUG(5, ("copy chunk no read on dest handle (%s).\n",
- smb_fname_str_dbg(dst_fsp->fsp_name) ));
- return NT_STATUS_ACCESS_DENIED;
- }
- /*
- * - The Open.GrantedAccess of the source file does not include
- * FILE_READ_DATA access.
- */
- if (!CHECK_READ_SMB2(src_fsp)) {
- DEBUG(5, ("copy chunk no read on src handle (%s).\n",
- smb_fname_str_dbg(src_fsp->fsp_name) ));
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (src_fsp->is_directory) {
- DEBUG(5, ("copy chunk no read on src directory handle (%s).\n",
- smb_fname_str_dbg(src_fsp->fsp_name) ));
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (dst_fsp->is_directory) {
- DEBUG(5, ("copy chunk no read on dst directory handle (%s).\n",
- smb_fname_str_dbg(dst_fsp->fsp_name) ));
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
- DEBUG(5, ("copy chunk no access on IPC$ handle.\n"));
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
- DEBUG(5, ("copy chunk no access on PRINT handle.\n"));
- return NT_STATUS_ACCESS_DENIED;
- }
-
- return NT_STATUS_OK;
-}
-
static NTSTATUS fsctl_srv_copychunk_loop(struct tevent_req *req);
static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
{
struct tevent_req *req = NULL;
struct fsctl_srv_copychunk_state *state = NULL;
- uint64_t src_persistent_h;
- uint64_t src_volatile_h;
enum ndr_err_code ndr_ret;
NTSTATUS status;
*state = (struct fsctl_srv_copychunk_state) {
.conn = dst_fsp->conn,
.ev = ev,
+ .ctl_code = ctl_code,
+ .dst_fsp = dst_fsp,
};
if (in_max_output < sizeof(struct srv_copychunk_rsp)) {
return tevent_req_post(req, ev);
}
- /* persistent/volatile keys sent as the resume key */
- src_persistent_h = BVAL(state->cc_copy.source_key, 0);
- src_volatile_h = BVAL(state->cc_copy.source_key, 8);
- state->src_fsp = file_fsp_get(smb2req, src_persistent_h, src_volatile_h);
- if (state->src_fsp == NULL) {
- DEBUG(3, ("invalid resume key in copy chunk req\n"));
- state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
- tevent_req_nterror(req, state->status);
- return tevent_req_post(req, ev);
- }
-
- state->dst_fsp = dst_fsp;
-
- state->status = copychunk_check_handles(ctl_code,
- state->src_fsp,
- state->dst_fsp);
- if (!NT_STATUS_IS_OK(state->status)) {
- tevent_req_nterror(req, state->status);
- return tevent_req_post(req, ev);
- }
+ state->token = data_blob_const(state->cc_copy.source_key,
+ sizeof(state->cc_copy.source_key));
state->status = copychunk_check_limits(&state->cc_copy);
if (!NT_STATUS_IS_OK(state->status)) {
subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
state,
state->ev,
- state->src_fsp,
+ state->ctl_code,
+ &state->token,
source_off,
state->dst_fsp,
target_off,
struct tevent_req *smb_vfs_call_offload_write_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
- struct files_struct *src_fsp,
- off_t src_off,
+ uint32_t fsctl,
+ DATA_BLOB *token,
+ off_t transfer_offset,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num,
uint32_t flags)
{
VFS_FIND(offload_write_send);
- return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, src_fsp,
- src_off, dest_fsp, dest_off, num,
+ return handle->fns->offload_write_send_fn(handle, mem_ctx, ev, fsctl,
+ token, transfer_offset,
+ dest_fsp, dest_off, num,
flags);
}