diff options
Diffstat (limited to 'source3/smbd/smb2_close.c')
-rw-r--r-- | source3/smbd/smb2_close.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/source3/smbd/smb2_close.c b/source3/smbd/smb2_close.c new file mode 100644 index 0000000..d4a2676 --- /dev/null +++ b/source3/smbd/smb2_close.c @@ -0,0 +1,415 @@ +/* + Unix SMB/CIFS implementation. + Core SMB2 server + + Copyright (C) Stefan Metzmacher 2009 + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "smbd/smbd.h" +#include "smbd/globals.h" +#include "../libcli/smb/smb_common.h" +#include "../lib/util/tevent_ntstatus.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_SMB2 + +static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req, + struct files_struct *in_fsp, + uint16_t in_flags); +static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req, + uint16_t *out_flags, + struct timespec *out_creation_ts, + struct timespec *out_last_access_ts, + struct timespec *out_last_write_ts, + struct timespec *out_change_ts, + uint64_t *out_allocation_size, + uint64_t *out_end_of_file, + uint32_t *out_file_attributes); + +static void smbd_smb2_request_close_done(struct tevent_req *subreq); + +NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req) +{ + const uint8_t *inbody; + uint16_t in_flags; + uint64_t in_file_id_persistent; + uint64_t in_file_id_volatile; + struct files_struct *in_fsp; + NTSTATUS status; + struct tevent_req *subreq; + + status = smbd_smb2_request_verify_sizes(req, 0x18); + if (!NT_STATUS_IS_OK(status)) { + return smbd_smb2_request_error(req, status); + } + inbody = SMBD_SMB2_IN_BODY_PTR(req); + + in_flags = SVAL(inbody, 0x02); + in_file_id_persistent = BVAL(inbody, 0x08); + in_file_id_volatile = BVAL(inbody, 0x10); + + in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); + if (in_fsp == NULL) { + return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); + } + + subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx, + req, in_fsp, in_flags); + if (subreq == NULL) { + return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + } + tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req); + + return smbd_smb2_request_pending_queue(req, subreq, 500); +} + +static void smbd_smb2_request_close_done(struct tevent_req *subreq) +{ + struct smbd_smb2_request *req = + tevent_req_callback_data(subreq, + struct smbd_smb2_request); + DATA_BLOB outbody; + uint16_t out_flags = 0; + connection_struct *conn = req->tcon->compat; + struct timespec out_creation_ts = { 0, }; + struct timespec out_last_access_ts = { 0, }; + struct timespec out_last_write_ts = { 0, }; + struct timespec out_change_ts = { 0, }; + uint64_t out_allocation_size = 0; + uint64_t out_end_of_file = 0; + uint32_t out_file_attributes = 0; + NTSTATUS status; + NTSTATUS error; + + status = smbd_smb2_close_recv(subreq, + &out_flags, + &out_creation_ts, + &out_last_access_ts, + &out_last_write_ts, + &out_change_ts, + &out_allocation_size, + &out_end_of_file, + &out_file_attributes); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + error = smbd_smb2_request_error(req, status); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(req->xconn, + nt_errstr(error)); + return; + } + return; + } + + outbody = smbd_smb2_generate_outbody(req, 0x3C); + if (outbody.data == NULL) { + error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(req->xconn, + nt_errstr(error)); + return; + } + return; + } + + SSVAL(outbody.data, 0x00, 0x3C); /* struct size */ + SSVAL(outbody.data, 0x02, out_flags); + SIVAL(outbody.data, 0x04, 0); /* reserved */ + put_long_date_full_timespec(conn->ts_res, + (char *)outbody.data + 0x08, &out_creation_ts); + put_long_date_full_timespec(conn->ts_res, + (char *)outbody.data + 0x10, &out_last_access_ts); + put_long_date_full_timespec(conn->ts_res, + (char *)outbody.data + 0x18, &out_last_write_ts); + put_long_date_full_timespec(conn->ts_res, + (char *)outbody.data + 0x20, &out_change_ts); + SBVAL(outbody.data, 0x28, out_allocation_size); + SBVAL(outbody.data, 0x30, out_end_of_file); + SIVAL(outbody.data, 0x38, out_file_attributes); + + error = smbd_smb2_request_done(req, outbody, NULL); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(req->xconn, + nt_errstr(error)); + return; + } +} + +static void setup_close_full_information(connection_struct *conn, + struct smb_filename *smb_fname, + struct timespec *out_creation_ts, + struct timespec *out_last_access_ts, + struct timespec *out_last_write_ts, + struct timespec *out_change_ts, + uint16_t *out_flags, + uint64_t *out_allocation_size, + uint64_t *out_end_of_file) +{ + *out_flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION; + *out_last_write_ts = smb_fname->st.st_ex_mtime; + *out_last_access_ts = smb_fname->st.st_ex_atime; + *out_creation_ts = get_create_timespec(conn, NULL, smb_fname); + *out_change_ts = get_change_timespec(conn, NULL, smb_fname); + + if (lp_dos_filetime_resolution(SNUM(conn))) { + dos_filetime_timespec(out_creation_ts); + dos_filetime_timespec(out_last_write_ts); + dos_filetime_timespec(out_last_access_ts); + dos_filetime_timespec(out_change_ts); + } + if (!S_ISDIR(smb_fname->st.st_ex_mode)) { + *out_end_of_file = get_file_size_stat(&smb_fname->st); + } + + *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st); +} + +static NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req, + struct files_struct **_fsp, + uint16_t in_flags, + uint16_t *out_flags, + struct timespec *out_creation_ts, + struct timespec *out_last_access_ts, + struct timespec *out_last_write_ts, + struct timespec *out_change_ts, + uint64_t *out_allocation_size, + uint64_t *out_end_of_file, + uint32_t *out_file_attributes) +{ + NTSTATUS status; + struct smb_request *smbreq; + connection_struct *conn = req->tcon->compat; + struct files_struct *fsp = *_fsp; + struct smb_filename *smb_fname = NULL; + + *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT}; + *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT}; + *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT}; + *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT}; + + *out_flags = 0; + *out_allocation_size = 0; + *out_end_of_file = 0; + *out_file_attributes = 0; + + DEBUG(10,("smbd_smb2_close: %s - %s\n", + fsp_str_dbg(fsp), fsp_fnum_dbg(fsp))); + + smbreq = smbd_smb2_fake_smb_request(req); + if (smbreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) { + *out_file_attributes = fdos_mode(fsp); + fsp->fsp_flags.fstat_before_close = true; + } + + status = close_file_smb(smbreq, fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n", + smb_fname_str_dbg(smb_fname), nt_errstr(status))); + file_free(smbreq, fsp); + *_fsp = fsp = NULL; + return status; + } + + if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) { + setup_close_full_information(conn, + fsp->fsp_name, + out_creation_ts, + out_last_access_ts, + out_last_write_ts, + out_change_ts, + out_flags, + out_allocation_size, + out_end_of_file); + } + + file_free(smbreq, fsp); + *_fsp = fsp = NULL; + return NT_STATUS_OK; +} + +struct smbd_smb2_close_state { + struct smbd_smb2_request *smb2req; + struct files_struct *in_fsp; + uint16_t in_flags; + uint16_t out_flags; + struct timespec out_creation_ts; + struct timespec out_last_access_ts; + struct timespec out_last_write_ts; + struct timespec out_change_ts; + uint64_t out_allocation_size; + uint64_t out_end_of_file; + uint32_t out_file_attributes; + struct tevent_queue *wait_queue; +}; + +static void smbd_smb2_close_wait_done(struct tevent_req *subreq); + +static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req, + struct files_struct *in_fsp, + uint16_t in_flags) +{ + struct tevent_req *req; + struct smbd_smb2_close_state *state; + unsigned i; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_close_state); + if (req == NULL) { + return NULL; + } + state->smb2req = smb2req; + state->in_fsp = in_fsp; + state->in_flags = in_flags; + + in_fsp->fsp_flags.closing = true; + + i = 0; + while (i < in_fsp->num_aio_requests) { + bool ok = tevent_req_cancel(in_fsp->aio_requests[i]); + if (ok) { + continue; + } + i += 1; + } + + if (in_fsp->num_aio_requests != 0) { + struct tevent_req *subreq; + + state->wait_queue = tevent_queue_create(state, + "smbd_smb2_close_send_wait_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + return tevent_req_post(req, ev); + } + /* + * Now wait until all aio requests on this fsp are + * finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of fsp->aio_request + * will remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(in_fsp->aio_requests, + smb2req->sconn->ev_ctx, + state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + /* + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are + * finished. + */ + subreq = tevent_queue_wait_send(state, + smb2req->sconn->ev_ctx, + state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req); + return req; + } + + status = smbd_smb2_close(smb2req, + &state->in_fsp, + state->in_flags, + &state->out_flags, + &state->out_creation_ts, + &state->out_last_access_ts, + &state->out_last_write_ts, + &state->out_change_ts, + &state->out_allocation_size, + &state->out_end_of_file, + &state->out_file_attributes); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static void smbd_smb2_close_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_smb2_close_state *state = tevent_req_data( + req, struct smbd_smb2_close_state); + NTSTATUS status; + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + + status = smbd_smb2_close(state->smb2req, + &state->in_fsp, + state->in_flags, + &state->out_flags, + &state->out_creation_ts, + &state->out_last_access_ts, + &state->out_last_write_ts, + &state->out_change_ts, + &state->out_allocation_size, + &state->out_end_of_file, + &state->out_file_attributes); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req, + uint16_t *out_flags, + struct timespec *out_creation_ts, + struct timespec *out_last_access_ts, + struct timespec *out_last_write_ts, + struct timespec *out_change_ts, + uint64_t *out_allocation_size, + uint64_t *out_end_of_file, + uint32_t *out_file_attributes) +{ + struct smbd_smb2_close_state *state = + tevent_req_data(req, + struct smbd_smb2_close_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + *out_flags = state->out_flags; + *out_creation_ts = state->out_creation_ts; + *out_last_access_ts = state->out_last_access_ts; + *out_last_write_ts = state->out_last_write_ts; + *out_change_ts = state->out_change_ts; + *out_allocation_size = state->out_allocation_size; + *out_end_of_file = state->out_end_of_file; + *out_file_attributes = state->out_file_attributes; + + tevent_req_received(req); + return NT_STATUS_OK; +} |