2 Unix SMB/CIFS implementation.
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/>.
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"
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
)
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
);
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
);
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;
109 NTSTATUS error
; /* transport error */
111 status
= smbd_smb2_oplock_break_recv(subreq
, &out_oplock_level
);
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
,
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
,
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
,
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
);
175 req
= tevent_req_create(mem_ctx
, &state
,
176 struct smbd_smb2_oplock_break_state
);
180 state
->smb2req
= smb2req
;
181 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_NONE
;
183 DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
185 fsp_str_dbg(fsp
), fsp_fnum_dbg(fsp
),
188 smbreq
= smbd_smb2_fake_smb_request(smb2req
);
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 "
195 (unsigned int)in_oplock_level
,
199 if ((fsp
->sent_oplock_break
== BREAK_TO_NONE_SENT
) ||
201 result
= remove_oplock(fsp
);
202 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_NONE
;
204 result
= downgrade_oplock(fsp
);
205 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_II
;
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
)
223 struct smbd_smb2_oplock_break_state
*state
=
225 struct smbd_smb2_oplock_break_state
);
227 if (tevent_req_is_nterror(req
, &status
)) {
228 tevent_req_received(req
);
232 *out_oplock_level
= state
->out_oplock_level
;
234 tevent_req_received(req
);
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
)
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;
287 NTSTATUS error
; /* transport error */
289 status
= smbd_smb2_lease_break_recv(subreq
, &out_lease_state
);
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
,
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
,
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
,
333 struct smbd_smb2_lease_break_state
{
334 uint32_t lease_state
;
337 struct lease_lookup_state
{
339 /* Return parameters. */
340 uint32_t num_file_ids
;
345 static void lease_parser(
347 const struct leases_db_file
*files
,
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
,
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
};
371 req
= tevent_req_create(mem_ctx
, &state
,
372 struct smbd_smb2_lease_break_state
);
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
,
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
,
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",
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
);
429 if (tevent_req_is_nterror(req
, &status
)) {
432 *out_lease_state
= state
->lease_state
;
436 /*********************************************************
437 Create and send an asynchronous
438 SMB2 OPLOCK_BREAK_NOTIFICATION.
439 *********************************************************/
441 void send_break_message_smb2(files_struct
*fsp
,
445 struct smbXsrv_client
*client
=
446 fsp
->conn
->sconn
->client
;
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",
454 (unsigned int)break_to
,
455 nt_errstr(fsp
->op
->status
));
459 DBG_DEBUG("sending oplock break "
460 "for file %s, %s, smb2 level %u\n",
463 (unsigned int)break_to
);
465 if (fsp
->oplock_type
== LEASE_OPLOCK
) {
466 uint32_t break_flags
= 0;
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
;
479 status
= smbd_smb2_send_lease_break(client
, new_epoch
, break_flags
,
480 &fsp
->lease
->lease
.lease_key
,
481 break_from
, break_to
);
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
,
490 if (!NT_STATUS_IS_OK(status
)) {
491 smbd_server_disconnect_client(client
,