smbXsrv_session: Remove a "can't happen" NULL check
[Samba.git] / source3 / smbd / smb2_break.c
blobf837b8eccc14584ec93122e743908a3bc7cca2a3
1 /*
2 Unix SMB/CIFS implementation.
3 Core SMB2 server
5 Copyright (C) Stefan Metzmacher 2009
6 Copyright (C) Jeremy Allison 2010
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 #include "locking/leases_db.h"
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_SMB2
32 static NTSTATUS smbd_smb2_request_process_lease_break(
33 struct smbd_smb2_request *req);
35 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
36 struct tevent_context *ev,
37 struct smbd_smb2_request *smb2req,
38 struct files_struct *in_fsp,
39 uint8_t in_oplock_level);
40 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
41 uint8_t *out_oplock_level);
43 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
44 NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
46 NTSTATUS status;
47 const uint8_t *inbody;
48 uint8_t in_oplock_level;
49 uint64_t in_file_id_persistent;
50 uint64_t in_file_id_volatile;
51 struct files_struct *in_fsp;
52 struct tevent_req *subreq;
54 status = smbd_smb2_request_verify_sizes(req, 0x18);
55 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
57 * Retry as a lease break
59 return smbd_smb2_request_process_lease_break(req);
61 if (!NT_STATUS_IS_OK(status)) {
62 return smbd_smb2_request_error(req, status);
64 inbody = SMBD_SMB2_IN_BODY_PTR(req);
66 in_oplock_level = CVAL(inbody, 0x02);
68 /* 0x03 1 bytes reserved */
69 /* 0x04 4 bytes reserved */
70 in_file_id_persistent = BVAL(inbody, 0x08);
71 in_file_id_volatile = BVAL(inbody, 0x10);
73 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
74 if (in_fsp == NULL) {
75 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
78 /* Are we awaiting a break message ? */
79 if (in_fsp->oplock_timeout == NULL) {
80 return smbd_smb2_request_error(
81 req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
84 if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
85 in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
86 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
89 subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
90 req, in_fsp, in_oplock_level);
91 if (subreq == NULL) {
92 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
94 tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
96 return smbd_smb2_request_pending_queue(req, subreq, 500);
99 static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
101 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
102 struct smbd_smb2_request);
103 const uint8_t *inbody;
104 uint64_t in_file_id_persistent;
105 uint64_t in_file_id_volatile;
106 uint8_t out_oplock_level = 0;
107 DATA_BLOB outbody;
108 NTSTATUS status;
109 NTSTATUS error; /* transport error */
111 status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
112 TALLOC_FREE(subreq);
113 if (!NT_STATUS_IS_OK(status)) {
114 error = smbd_smb2_request_error(req, status);
115 if (!NT_STATUS_IS_OK(error)) {
116 smbd_server_connection_terminate(req->xconn,
117 nt_errstr(error));
118 return;
120 return;
123 inbody = SMBD_SMB2_IN_BODY_PTR(req);
125 in_file_id_persistent = BVAL(inbody, 0x08);
126 in_file_id_volatile = BVAL(inbody, 0x10);
128 outbody = smbd_smb2_generate_outbody(req, 0x18);
129 if (outbody.data == NULL) {
130 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
131 if (!NT_STATUS_IS_OK(error)) {
132 smbd_server_connection_terminate(req->xconn,
133 nt_errstr(error));
134 return;
136 return;
139 SSVAL(outbody.data, 0x00, 0x18); /* struct size */
140 SCVAL(outbody.data, 0x02,
141 out_oplock_level); /* SMB2 oplock level */
142 SCVAL(outbody.data, 0x03, 0); /* reserved */
143 SIVAL(outbody.data, 0x04, 0); /* reserved */
144 SBVAL(outbody.data, 0x08,
145 in_file_id_persistent); /* file id (persistent) */
146 SBVAL(outbody.data, 0x10,
147 in_file_id_volatile); /* file id (volatile) */
149 error = smbd_smb2_request_done(req, outbody, NULL);
150 if (!NT_STATUS_IS_OK(error)) {
151 smbd_server_connection_terminate(req->xconn,
152 nt_errstr(error));
153 return;
157 struct smbd_smb2_oplock_break_state {
158 struct smbd_smb2_request *smb2req;
159 uint8_t out_oplock_level; /* SMB2 oplock level. */
162 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
163 struct tevent_context *ev,
164 struct smbd_smb2_request *smb2req,
165 struct files_struct *fsp,
166 uint8_t in_oplock_level)
168 struct tevent_req *req;
169 struct smbd_smb2_oplock_break_state *state;
170 struct smb_request *smbreq;
171 int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
172 bool break_to_none = (oplocklevel == NO_OPLOCK);
173 bool result;
175 req = tevent_req_create(mem_ctx, &state,
176 struct smbd_smb2_oplock_break_state);
177 if (req == NULL) {
178 return NULL;
180 state->smb2req = smb2req;
181 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
183 DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
184 "samba level %d\n",
185 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
186 oplocklevel));
188 smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
189 if (tevent_req_nomem(smbreq, req)) {
190 return tevent_req_post(req, ev);
193 DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
194 "for file %s, %s\n",
195 (unsigned int)in_oplock_level,
196 fsp_str_dbg(fsp),
197 fsp_fnum_dbg(fsp)));
199 if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
200 (break_to_none)) {
201 result = remove_oplock(fsp);
202 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
203 } else {
204 result = downgrade_oplock(fsp);
205 state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
208 if (!result) {
209 DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
210 "oplock on file %s\n", fsp_str_dbg(fsp)));
211 /* Hmmm. Is this panic justified? */
212 smb_panic("internal tdb error");
215 tevent_req_done(req);
216 return tevent_req_post(req, ev);
219 static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
220 uint8_t *out_oplock_level)
222 NTSTATUS status;
223 struct smbd_smb2_oplock_break_state *state =
224 tevent_req_data(req,
225 struct smbd_smb2_oplock_break_state);
227 if (tevent_req_is_nterror(req, &status)) {
228 tevent_req_received(req);
229 return status;
232 *out_oplock_level = state->out_oplock_level;
234 tevent_req_received(req);
235 return NT_STATUS_OK;
238 static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
240 static struct tevent_req *smbd_smb2_lease_break_send(
241 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
242 struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
243 uint32_t in_lease_state);
244 static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
245 uint32_t *out_lease_state);
248 static NTSTATUS smbd_smb2_request_process_lease_break(
249 struct smbd_smb2_request *req)
251 NTSTATUS status;
252 const uint8_t *inbody;
253 struct smb2_lease_key in_lease_key;
254 uint32_t in_lease_state;
255 struct tevent_req *subreq;
257 status = smbd_smb2_request_verify_sizes(req, 0x24);
258 if (!NT_STATUS_IS_OK(status)) {
259 return smbd_smb2_request_error(req, status);
262 inbody = SMBD_SMB2_IN_BODY_PTR(req);
264 in_lease_key.data[0] = BVAL(inbody, 8);
265 in_lease_key.data[1] = BVAL(inbody, 16);
266 in_lease_state = IVAL(inbody, 24);
268 subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
269 in_lease_key, in_lease_state);
270 if (subreq == NULL) {
271 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
273 tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
275 return smbd_smb2_request_pending_queue(req, subreq, 500);
278 static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
280 struct smbd_smb2_request *req = tevent_req_callback_data(
281 subreq, struct smbd_smb2_request);
282 const uint8_t *inbody;
283 struct smb2_lease_key in_lease_key;
284 uint32_t out_lease_state = 0;
285 DATA_BLOB outbody;
286 NTSTATUS status;
287 NTSTATUS error; /* transport error */
289 status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
290 TALLOC_FREE(subreq);
291 if (!NT_STATUS_IS_OK(status)) {
292 error = smbd_smb2_request_error(req, status);
293 if (!NT_STATUS_IS_OK(error)) {
294 smbd_server_connection_terminate(req->xconn,
295 nt_errstr(error));
296 return;
298 return;
301 inbody = SMBD_SMB2_IN_BODY_PTR(req);
303 in_lease_key.data[0] = BVAL(inbody, 8);
304 in_lease_key.data[1] = BVAL(inbody, 16);
306 outbody = smbd_smb2_generate_outbody(req, 0x24);
307 if (outbody.data == NULL) {
308 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
309 if (!NT_STATUS_IS_OK(error)) {
310 smbd_server_connection_terminate(req->xconn,
311 nt_errstr(error));
312 return;
314 return;
317 SSVAL(outbody.data, 0x00, 0x24); /* struct size */
318 SSVAL(outbody.data, 0x02, 0); /* reserved */
319 SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */
320 SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
321 SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
322 SIVAL(outbody.data, 0x18, out_lease_state);
323 SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */
325 error = smbd_smb2_request_done(req, outbody, NULL);
326 if (!NT_STATUS_IS_OK(error)) {
327 smbd_server_connection_terminate(req->xconn,
328 nt_errstr(error));
329 return;
333 struct smbd_smb2_lease_break_state {
334 uint32_t lease_state;
337 struct lease_lookup_state {
338 TALLOC_CTX *mem_ctx;
339 /* Return parameters. */
340 uint32_t num_file_ids;
341 struct file_id *ids;
342 NTSTATUS status;
345 static void lease_parser(
346 uint32_t num_files,
347 const struct leases_db_file *files,
348 void *private_data)
350 struct lease_lookup_state *lls =
351 (struct lease_lookup_state *)private_data;
353 lls->status = NT_STATUS_OK;
354 lls->num_file_ids = num_files;
355 lls->status = leases_db_copy_file_ids(lls->mem_ctx,
356 num_files,
357 files,
358 &lls->ids);
361 static struct tevent_req *smbd_smb2_lease_break_send(
362 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
363 struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
364 uint32_t in_lease_state)
366 struct tevent_req *req;
367 struct smbd_smb2_lease_break_state *state;
368 struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
369 NTSTATUS status;
371 req = tevent_req_create(mem_ctx, &state,
372 struct smbd_smb2_lease_break_state);
373 if (req == NULL) {
374 return NULL;
376 state->lease_state = in_lease_state;
378 /* Find any file ids with this lease key. */
379 status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
380 &in_lease_key,
381 lease_parser,
382 &lls);
384 if (!NT_STATUS_IS_OK(status)) {
385 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
386 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
387 DEBUG(10, ("No record for lease key found\n"));
389 tevent_req_nterror(req, status);
390 return tevent_req_post(req, ev);
393 if (tevent_req_nterror(req, lls.status)) {
394 return tevent_req_post(req, ev);
397 if (lls.num_file_ids == 0) {
398 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
399 return tevent_req_post(req, ev);
402 status = downgrade_lease(smb2_req->xconn->client,
403 lls.num_file_ids,
404 lls.ids,
405 &in_lease_key,
406 in_lease_state);
408 if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
409 tevent_req_done(req);
410 return tevent_req_post(req, ev);
412 if (tevent_req_nterror(req, status)) {
413 DEBUG(10, ("downgrade_lease returned %s\n",
414 nt_errstr(status)));
415 return tevent_req_post(req, ev);
418 tevent_req_done(req);
419 return tevent_req_post(req, ev);
422 static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
423 uint32_t *out_lease_state)
425 struct smbd_smb2_lease_break_state *state = tevent_req_data(
426 req, struct smbd_smb2_lease_break_state);
427 NTSTATUS status;
429 if (tevent_req_is_nterror(req, &status)) {
430 return status;
432 *out_lease_state = state->lease_state;
433 return NT_STATUS_OK;
436 /*********************************************************
437 Create and send an asynchronous
438 SMB2 OPLOCK_BREAK_NOTIFICATION.
439 *********************************************************/
441 void send_break_message_smb2(files_struct *fsp,
442 uint32_t break_from,
443 uint32_t break_to)
445 struct smbXsrv_client *client =
446 fsp->conn->sconn->client;
447 NTSTATUS status;
449 if (!NT_STATUS_IS_OK(fsp->op->status)) {
450 DBG_DEBUG("skip oplock break for file %s, %s, "
451 "smb2 level %u fsp status=%s\n",
452 fsp_str_dbg(fsp),
453 fsp_fnum_dbg(fsp),
454 (unsigned int)break_to,
455 nt_errstr(fsp->op->status));
456 return;
459 DBG_DEBUG("sending oplock break "
460 "for file %s, %s, smb2 level %u\n",
461 fsp_str_dbg(fsp),
462 fsp_fnum_dbg(fsp),
463 (unsigned int)break_to);
465 if (fsp->oplock_type == LEASE_OPLOCK) {
466 uint32_t break_flags = 0;
467 uint16_t new_epoch;
469 if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
470 break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
473 if (fsp->lease->lease.lease_version > 1) {
474 new_epoch = fsp->lease->lease.lease_epoch;
475 } else {
476 new_epoch = 0;
479 status = smbd_smb2_send_lease_break(client, new_epoch, break_flags,
480 &fsp->lease->lease.lease_key,
481 break_from, break_to);
482 } else {
483 uint8_t smb2_oplock_level;
484 smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
485 SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
486 status = smbd_smb2_send_oplock_break(client,
487 fsp->op,
488 smb2_oplock_level);
490 if (!NT_STATUS_IS_OK(status)) {
491 smbd_server_disconnect_client(client,
492 nt_errstr(status));
493 return;