X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fsmb2_notify.c;h=68429b7b766a324f61ac45047aec6d238a81a2e7;hb=11f2583420310e0278188935f31be3131eb85fd4;hp=4b9cbc495b56a377648125231b74d38867bc9c73;hpb=79fd60dd3872f136528821d1c755f55fa60b3450;p=samba.git diff --git a/source3/smbd/smb2_notify.c b/source3/smbd/smb2_notify.c index 4b9cbc495b5..68429b7b766 100644 --- a/source3/smbd/smb2_notify.c +++ b/source3/smbd/smb2_notify.c @@ -3,6 +3,7 @@ Core SMB2 server Copyright (C) Stefan Metzmacher 2009 + Copyright (C) Jeremy Allison 2010 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 @@ -19,15 +20,29 @@ */ #include "includes.h" +#include "smbd/smbd.h" #include "smbd/globals.h" -#include "../source4/libcli/smb2/smb2_constants.h" +#include "../libcli/smb/smb_common.h" +#include "../lib/util/tevent_ntstatus.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_SMB2 + +struct smbd_smb2_notify_state { + struct smbd_smb2_request *smb2req; + struct smb_request *smbreq; + bool has_request; + bool skip_reply; + NTSTATUS status; + DATA_BLOB out_output_buffer; +}; static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req, + struct files_struct *in_fsp, uint16_t in_flags, uint32_t in_output_buffer_length, - uint64_t in_file_id_volatile, uint64_t in_completion_filter); static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, @@ -36,29 +51,22 @@ static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req, static void smbd_smb2_request_notify_done(struct tevent_req *subreq); NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req) { - const uint8_t *inhdr; + struct smbXsrv_connection *xconn = req->xconn; + NTSTATUS status; const uint8_t *inbody; - int i = req->current_idx; - size_t expected_body_size = 0x20; - size_t body_size; uint16_t in_flags; uint32_t in_output_buffer_length; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; + struct files_struct *in_fsp; uint64_t in_completion_filter; struct tevent_req *subreq; - inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; - if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { - return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); - } - - inbody = (const uint8_t *)req->in.vector[i+1].iov_base; - - body_size = SVAL(inbody, 0x00); - if (body_size != expected_body_size) { - return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); + status = smbd_smb2_request_verify_sizes(req, 0x20); + 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_output_buffer_length = IVAL(inbody, 0x04); @@ -70,41 +78,39 @@ NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req) * 0x00010000 is what Windows 7 uses, * Windows 2008 uses 0x00080000 */ - if (in_output_buffer_length > 0x00010000) { + if (in_output_buffer_length > xconn->smb2.server.max_trans) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } - if (req->compat_chain_fsp) { - /* skip check */ - } else if (in_file_id_persistent != 0) { + status = smbd_smb2_request_verify_creditcharge(req, + in_output_buffer_length); + + if (!NT_STATUS_IS_OK(status)) { + return smbd_smb2_request_error(req, status); + } + + 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_notify_send(req, - req->conn->smb2.event_ctx, - req, + subreq = smbd_smb2_notify_send(req, req->sconn->ev_ctx, + req, in_fsp, in_flags, in_output_buffer_length, - in_file_id_volatile, in_completion_filter); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req); - if (tevent_req_is_in_progress(subreq)) { - return smbd_smb2_request_pending_queue(req); - } - - return NT_STATUS_OK; + return smbd_smb2_request_pending_queue(req, subreq, 500); } static void smbd_smb2_request_notify_done(struct tevent_req *subreq) { struct smbd_smb2_request *req = tevent_req_callback_data(subreq, struct smbd_smb2_request); - int i = req->current_idx; - uint8_t *outhdr; DATA_BLOB outbody; DATA_BLOB outdyn; uint16_t out_output_buffer_offset; @@ -119,7 +125,7 @@ static void smbd_smb2_request_notify_done(struct tevent_req *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->conn, + smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } @@ -128,13 +134,11 @@ static void smbd_smb2_request_notify_done(struct tevent_req *subreq) out_output_buffer_offset = SMB2_HDR_BODY + 0x08; - outhdr = (uint8_t *)req->out.vector[i].iov_base; - - outbody = data_blob_talloc(req->out.vector, NULL, 0x08); + outbody = smbd_smb2_generate_outbody(req, 0x08); 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->conn, + smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } @@ -151,40 +155,68 @@ static void smbd_smb2_request_notify_done(struct tevent_req *subreq) error = smbd_smb2_request_done(req, outbody, &outdyn); if (!NT_STATUS_IS_OK(error)) { - smbd_server_connection_terminate(req->conn, + smbd_server_connection_terminate(req->xconn, nt_errstr(error)); return; } } -struct smbd_smb2_notify_state { - struct smbd_smb2_request *smb2req; - struct tevent_immediate *im; - NTSTATUS status; - DATA_BLOB out_output_buffer; -}; - static void smbd_smb2_notify_reply(struct smb_request *smbreq, NTSTATUS error_code, uint8_t *buf, size_t len); -static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx, - struct tevent_immediate *im, - void *private_data); +static bool smbd_smb2_notify_cancel(struct tevent_req *req); + +static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state) +{ + if (!state->has_request) { + return 0; + } + + state->skip_reply = true; + smbd_notify_cancel_by_smbreq(state->smbreq); + return 0; +} + +static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq) +{ + struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv, + struct tevent_req); + struct smbd_smb2_notify_state *state = tevent_req_data(req, + struct smbd_smb2_notify_state); + + /* + * Our temporary parent from change_notify_add_request() + * goes away. + */ + state->has_request = false; + + /* + * move it back to its original parent, + * which means we no longer need the destructor + * to protect it. + */ + talloc_steal(smbreq->smb2req, smbreq); + talloc_set_destructor(smbreq, NULL); + + /* + * We want to keep smbreq! + */ + return -1; +} static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req, + struct files_struct *fsp, uint16_t in_flags, uint32_t in_output_buffer_length, - uint64_t in_file_id_volatile, uint64_t in_completion_filter) { struct tevent_req *req; struct smbd_smb2_notify_state *state; struct smb_request *smbreq; - connection_struct *conn = smb2req->tcon->compat_conn; - files_struct *fsp; - bool recursive = (in_flags & 0x0001) ? true : false; + connection_struct *conn = smb2req->tcon->compat; + bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false; NTSTATUS status; req = tevent_req_create(mem_ctx, &state, @@ -195,33 +227,20 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, state->smb2req = smb2req; state->status = NT_STATUS_INTERNAL_ERROR; state->out_output_buffer = data_blob_null; - state->im = NULL; + talloc_set_destructor(state, smbd_smb2_notify_state_destructor); - DEBUG(10,("smbd_smb2_notify_send: file_id[0x%016llX]\n", - (unsigned long long)in_file_id_volatile)); + DEBUG(10,("smbd_smb2_notify_send: %s - %s\n", + fsp_str_dbg(fsp), fsp_fnum_dbg(fsp))); smbreq = smbd_smb2_fake_smb_request(smb2req); if (tevent_req_nomem(smbreq, req)) { return tevent_req_post(req, ev); } + state->smbreq = smbreq; smbreq->async_priv = (void *)req; - fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); - if (fsp == NULL) { - tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); - return tevent_req_post(req, ev); - } - if (conn != fsp->conn) { - tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); - return tevent_req_post(req, ev); - } - if (smb2req->session->vuid != fsp->vuid) { - tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); - return tevent_req_post(req, ev); - } - - { + if (DEBUGLEVEL >= 3) { char *filter_string; filter_string = notify_filter_string(NULL, in_completion_filter); @@ -244,6 +263,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, if (fsp->notify == NULL) { status = change_notify_create(fsp, + in_output_buffer_length, in_completion_filter, recursive); if (!NT_STATUS_IS_OK(status)) { @@ -254,7 +274,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, } } - if (fsp->notify->num_changes != 0) { + if (change_notify_fsp_has_changes(fsp)) { /* * We've got changes pending, respond immediately @@ -265,7 +285,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, * here. */ - change_notify_reply(fsp->conn, smbreq, + change_notify_reply(smbreq, NT_STATUS_OK, in_output_buffer_length, fsp->notify, @@ -278,11 +298,6 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } - state->im = tevent_create_immediate(state); - if (tevent_req_nomem(state->im, req)) { - return tevent_req_post(req, ev); - } - /* * No changes pending, queue the request */ @@ -297,6 +312,20 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } + /* + * This is a HACK! + * + * change_notify_add_request() talloc_moves() + * smbreq away from us, so we need a destructor + * which moves it back at the end. + */ + state->has_request = true; + talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor); + + /* allow this request to be canceled */ + tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel); + + SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile); return req; } @@ -309,6 +338,12 @@ static void smbd_smb2_notify_reply(struct smb_request *smbreq, struct smbd_smb2_notify_state *state = tevent_req_data(req, struct smbd_smb2_notify_state); + if (state->skip_reply) { + return; + } + + SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile); + state->status = error_code; if (!NT_STATUS_IS_OK(error_code)) { /* nothing */ @@ -321,37 +356,24 @@ static void smbd_smb2_notify_reply(struct smb_request *smbreq, } } - if (state->im == NULL) { - smbd_smb2_notify_reply_trigger(NULL, NULL, req); + tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx); + + if (!NT_STATUS_IS_OK(state->status)) { + tevent_req_nterror(req, state->status); return; } - /* - * if this is called async, we need to go via an immediate event - * because the caller replies on the smb_request (a child of req - * being arround after calling this function - */ - tevent_schedule_immediate(state->im, - state->smb2req->conn->smb2.event_ctx, - smbd_smb2_notify_reply_trigger, - req); + tevent_req_done(req); } -static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx, - struct tevent_immediate *im, - void *private_data) +static bool smbd_smb2_notify_cancel(struct tevent_req *req) { - struct tevent_req *req = talloc_get_type_abort(private_data, - struct tevent_req); struct smbd_smb2_notify_state *state = tevent_req_data(req, struct smbd_smb2_notify_state); - if (!NT_STATUS_IS_OK(state->status)) { - tevent_req_nterror(req, state->status); - return; - } + smbd_notify_cancel_by_smbreq(state->smbreq); - tevent_req_done(req); + return true; } static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,