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"
29 static NTSTATUS
smbd_smb2_request_process_lease_break(
30 struct smbd_smb2_request
*req
);
32 static struct tevent_req
*smbd_smb2_oplock_break_send(TALLOC_CTX
*mem_ctx
,
33 struct tevent_context
*ev
,
34 struct smbd_smb2_request
*smb2req
,
35 struct files_struct
*in_fsp
,
36 uint8_t in_oplock_level
);
37 static NTSTATUS
smbd_smb2_oplock_break_recv(struct tevent_req
*req
,
38 uint8_t *out_oplock_level
);
40 static void smbd_smb2_request_oplock_break_done(struct tevent_req
*subreq
);
41 NTSTATUS
smbd_smb2_request_process_break(struct smbd_smb2_request
*req
)
44 const uint8_t *inbody
;
45 uint8_t in_oplock_level
;
46 uint64_t in_file_id_persistent
;
47 uint64_t in_file_id_volatile
;
48 struct files_struct
*in_fsp
;
49 struct tevent_req
*subreq
;
51 status
= smbd_smb2_request_verify_sizes(req
, 0x18);
52 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
54 * Retry as a lease break
56 return smbd_smb2_request_process_lease_break(req
);
58 if (!NT_STATUS_IS_OK(status
)) {
59 return smbd_smb2_request_error(req
, status
);
61 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
63 in_oplock_level
= CVAL(inbody
, 0x02);
65 /* 0x03 1 bytes reserved */
66 /* 0x04 4 bytes reserved */
67 in_file_id_persistent
= BVAL(inbody
, 0x08);
68 in_file_id_volatile
= BVAL(inbody
, 0x10);
70 in_fsp
= file_fsp_smb2(req
, in_file_id_persistent
, in_file_id_volatile
);
72 return smbd_smb2_request_error(req
, NT_STATUS_FILE_CLOSED
);
75 /* Are we awaiting a break message ? */
76 if (in_fsp
->oplock_timeout
== NULL
) {
77 return smbd_smb2_request_error(
78 req
, NT_STATUS_INVALID_OPLOCK_PROTOCOL
);
81 if (in_oplock_level
!= SMB2_OPLOCK_LEVEL_NONE
&&
82 in_oplock_level
!= SMB2_OPLOCK_LEVEL_II
) {
83 return smbd_smb2_request_error(req
, NT_STATUS_INVALID_PARAMETER
);
86 subreq
= smbd_smb2_oplock_break_send(req
, req
->sconn
->ev_ctx
,
87 req
, in_fsp
, in_oplock_level
);
89 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
91 tevent_req_set_callback(subreq
, smbd_smb2_request_oplock_break_done
, req
);
93 return smbd_smb2_request_pending_queue(req
, subreq
, 500);
96 static void smbd_smb2_request_oplock_break_done(struct tevent_req
*subreq
)
98 struct smbd_smb2_request
*req
= tevent_req_callback_data(subreq
,
99 struct smbd_smb2_request
);
100 const uint8_t *inbody
;
101 uint64_t in_file_id_persistent
;
102 uint64_t in_file_id_volatile
;
103 uint8_t out_oplock_level
= 0;
106 NTSTATUS error
; /* transport error */
108 status
= smbd_smb2_oplock_break_recv(subreq
, &out_oplock_level
);
110 if (!NT_STATUS_IS_OK(status
)) {
111 error
= smbd_smb2_request_error(req
, status
);
112 if (!NT_STATUS_IS_OK(error
)) {
113 smbd_server_connection_terminate(req
->xconn
,
120 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
122 in_file_id_persistent
= BVAL(inbody
, 0x08);
123 in_file_id_volatile
= BVAL(inbody
, 0x10);
125 outbody
= smbd_smb2_generate_outbody(req
, 0x18);
126 if (outbody
.data
== NULL
) {
127 error
= smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
128 if (!NT_STATUS_IS_OK(error
)) {
129 smbd_server_connection_terminate(req
->xconn
,
136 SSVAL(outbody
.data
, 0x00, 0x18); /* struct size */
137 SCVAL(outbody
.data
, 0x02,
138 out_oplock_level
); /* SMB2 oplock level */
139 SCVAL(outbody
.data
, 0x03, 0); /* reserved */
140 SIVAL(outbody
.data
, 0x04, 0); /* reserved */
141 SBVAL(outbody
.data
, 0x08,
142 in_file_id_persistent
); /* file id (persistent) */
143 SBVAL(outbody
.data
, 0x10,
144 in_file_id_volatile
); /* file id (volatile) */
146 error
= smbd_smb2_request_done(req
, outbody
, NULL
);
147 if (!NT_STATUS_IS_OK(error
)) {
148 smbd_server_connection_terminate(req
->xconn
,
154 struct smbd_smb2_oplock_break_state
{
155 struct smbd_smb2_request
*smb2req
;
156 uint8_t out_oplock_level
; /* SMB2 oplock level. */
159 static struct tevent_req
*smbd_smb2_oplock_break_send(TALLOC_CTX
*mem_ctx
,
160 struct tevent_context
*ev
,
161 struct smbd_smb2_request
*smb2req
,
162 struct files_struct
*fsp
,
163 uint8_t in_oplock_level
)
165 struct tevent_req
*req
;
166 struct smbd_smb2_oplock_break_state
*state
;
167 struct smb_request
*smbreq
;
168 int oplocklevel
= map_smb2_oplock_levels_to_samba(in_oplock_level
);
169 bool break_to_none
= (oplocklevel
== NO_OPLOCK
);
172 req
= tevent_req_create(mem_ctx
, &state
,
173 struct smbd_smb2_oplock_break_state
);
177 state
->smb2req
= smb2req
;
178 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_NONE
;
180 DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
182 fsp_str_dbg(fsp
), fsp_fnum_dbg(fsp
),
185 smbreq
= smbd_smb2_fake_smb_request(smb2req
);
186 if (tevent_req_nomem(smbreq
, req
)) {
187 return tevent_req_post(req
, ev
);
190 DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
192 (unsigned int)in_oplock_level
,
196 if ((fsp
->sent_oplock_break
== BREAK_TO_NONE_SENT
) ||
198 result
= remove_oplock(fsp
);
199 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_NONE
;
201 result
= downgrade_oplock(fsp
);
202 state
->out_oplock_level
= SMB2_OPLOCK_LEVEL_II
;
206 DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
207 "oplock on file %s\n", fsp_str_dbg(fsp
)));
208 /* Hmmm. Is this panic justified? */
209 smb_panic("internal tdb error");
212 tevent_req_done(req
);
213 return tevent_req_post(req
, ev
);
216 static NTSTATUS
smbd_smb2_oplock_break_recv(struct tevent_req
*req
,
217 uint8_t *out_oplock_level
)
220 struct smbd_smb2_oplock_break_state
*state
=
222 struct smbd_smb2_oplock_break_state
);
224 if (tevent_req_is_nterror(req
, &status
)) {
225 tevent_req_received(req
);
229 *out_oplock_level
= state
->out_oplock_level
;
231 tevent_req_received(req
);
235 static void smbd_smb2_request_lease_break_done(struct tevent_req
*subreq
);
237 static struct tevent_req
*smbd_smb2_lease_break_send(
238 TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
239 struct smbd_smb2_request
*smb2_req
, struct smb2_lease_key in_lease_key
,
240 uint32_t in_lease_state
);
241 static NTSTATUS
smbd_smb2_lease_break_recv(struct tevent_req
*req
,
242 uint32_t *out_lease_state
);
245 static NTSTATUS
smbd_smb2_request_process_lease_break(
246 struct smbd_smb2_request
*req
)
249 const uint8_t *inbody
;
250 struct smb2_lease_key in_lease_key
;
251 uint32_t in_lease_state
;
252 struct tevent_req
*subreq
;
254 status
= smbd_smb2_request_verify_sizes(req
, 0x24);
255 if (!NT_STATUS_IS_OK(status
)) {
256 return smbd_smb2_request_error(req
, status
);
259 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
261 in_lease_key
.data
[0] = BVAL(inbody
, 8);
262 in_lease_key
.data
[1] = BVAL(inbody
, 16);
263 in_lease_state
= IVAL(inbody
, 24);
265 subreq
= smbd_smb2_lease_break_send(req
, req
->sconn
->ev_ctx
, req
,
266 in_lease_key
, in_lease_state
);
267 if (subreq
== NULL
) {
268 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
270 tevent_req_set_callback(subreq
, smbd_smb2_request_lease_break_done
, req
);
272 return smbd_smb2_request_pending_queue(req
, subreq
, 500);
275 static void smbd_smb2_request_lease_break_done(struct tevent_req
*subreq
)
277 struct smbd_smb2_request
*req
= tevent_req_callback_data(
278 subreq
, struct smbd_smb2_request
);
279 const uint8_t *inbody
;
280 struct smb2_lease_key in_lease_key
;
281 uint32_t out_lease_state
= 0;
284 NTSTATUS error
; /* transport error */
286 status
= smbd_smb2_lease_break_recv(subreq
, &out_lease_state
);
288 if (!NT_STATUS_IS_OK(status
)) {
289 error
= smbd_smb2_request_error(req
, status
);
290 if (!NT_STATUS_IS_OK(error
)) {
291 smbd_server_connection_terminate(req
->xconn
,
298 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
300 in_lease_key
.data
[0] = BVAL(inbody
, 8);
301 in_lease_key
.data
[1] = BVAL(inbody
, 16);
303 outbody
= smbd_smb2_generate_outbody(req
, 0x24);
304 if (outbody
.data
== NULL
) {
305 error
= smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
306 if (!NT_STATUS_IS_OK(error
)) {
307 smbd_server_connection_terminate(req
->xconn
,
314 SSVAL(outbody
.data
, 0x00, 0x24); /* struct size */
315 SSVAL(outbody
.data
, 0x02, 0); /* reserved */
316 SIVAL(outbody
.data
, 0x04, 0); /* flags, must be 0 */
317 SBVAL(outbody
.data
, 0x08, in_lease_key
.data
[0]);
318 SBVAL(outbody
.data
, 0x10, in_lease_key
.data
[1]);
319 SIVAL(outbody
.data
, 0x18, out_lease_state
);
320 SBVAL(outbody
.data
, 0x1c, 0); /* leaseduration, must be 0 */
322 error
= smbd_smb2_request_done(req
, outbody
, NULL
);
323 if (!NT_STATUS_IS_OK(error
)) {
324 smbd_server_connection_terminate(req
->xconn
,
330 struct smbd_smb2_lease_break_state
{
331 uint32_t lease_state
;
334 struct lease_lookup_state
{
336 /* Return parameters. */
337 uint32_t num_file_ids
;
342 static void lease_parser(
344 const struct leases_db_file
*files
,
347 struct lease_lookup_state
*lls
=
348 (struct lease_lookup_state
*)private_data
;
350 lls
->status
= NT_STATUS_OK
;
351 lls
->num_file_ids
= num_files
;
352 lls
->status
= leases_db_copy_file_ids(lls
->mem_ctx
,
358 static struct tevent_req
*smbd_smb2_lease_break_send(
359 TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
360 struct smbd_smb2_request
*smb2_req
, struct smb2_lease_key in_lease_key
,
361 uint32_t in_lease_state
)
363 struct tevent_req
*req
;
364 struct smbd_smb2_lease_break_state
*state
;
365 struct lease_lookup_state lls
= {.mem_ctx
= mem_ctx
};
368 req
= tevent_req_create(mem_ctx
, &state
,
369 struct smbd_smb2_lease_break_state
);
373 state
->lease_state
= in_lease_state
;
375 /* Find any file ids with this lease key. */
376 status
= leases_db_parse(&smb2_req
->xconn
->smb2
.client
.guid
,
381 if (!NT_STATUS_IS_OK(status
)) {
382 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
383 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
384 DEBUG(10, ("No record for lease key found\n"));
386 } else if (!NT_STATUS_IS_OK(lls
.status
)) {
388 } else if (lls
.num_file_ids
== 0) {
389 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
392 if (!NT_STATUS_IS_OK(status
)) {
393 tevent_req_nterror(req
, status
);
394 return tevent_req_post(req
, ev
);
397 status
= downgrade_lease(smb2_req
->xconn
,
403 if (NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
)) {
404 tevent_req_done(req
);
405 return tevent_req_post(req
, ev
);
407 if (tevent_req_nterror(req
, status
)) {
408 DEBUG(10, ("downgrade_lease returned %s\n",
410 return tevent_req_post(req
, ev
);
413 tevent_req_done(req
);
414 return tevent_req_post(req
, ev
);
417 static NTSTATUS
smbd_smb2_lease_break_recv(struct tevent_req
*req
,
418 uint32_t *out_lease_state
)
420 struct smbd_smb2_lease_break_state
*state
= tevent_req_data(
421 req
, struct smbd_smb2_lease_break_state
);
424 if (tevent_req_is_nterror(req
, &status
)) {
427 *out_lease_state
= state
->lease_state
;
431 /*********************************************************
432 Create and send an asynchronous
433 SMB2 OPLOCK_BREAK_NOTIFICATION.
434 *********************************************************/
436 void send_break_message_smb2(files_struct
*fsp
,
441 struct smbXsrv_connection
*xconn
= NULL
;
442 struct smbXsrv_session
*session
= NULL
;
443 struct timeval tv
= timeval_current();
444 NTTIME now
= timeval_to_nttime(&tv
);
447 * TODO: in future we should have a better algorithm
448 * to find the correct connection for a break message.
449 * Then we also need some retries if a channel gets disconnected.
451 xconn
= fsp
->conn
->sconn
->client
->connections
;
453 status
= smb2srv_session_lookup_conn(xconn
,
457 if (NT_STATUS_EQUAL(status
, NT_STATUS_USER_SESSION_DELETED
) ||
461 DEBUG(10,("send_break_message_smb2: skip oplock break "
462 "for file %s, %s, smb2 level %u session %llu not found\n",
465 (unsigned int)break_to
,
466 (unsigned long long)fsp
->vuid
));
470 DEBUG(10,("send_break_message_smb2: sending oplock break "
471 "for file %s, %s, smb2 level %u\n",
474 (unsigned int)break_to
));
476 if (fsp
->oplock_type
== LEASE_OPLOCK
) {
477 uint32_t break_flags
= 0;
480 if (fsp
->lease
->lease
.lease_state
!= SMB2_LEASE_NONE
) {
481 break_flags
= SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED
;
484 if (fsp
->lease
->lease
.lease_version
> 1) {
485 new_epoch
= fsp
->lease
->lease
.lease_epoch
;
490 status
= smbd_smb2_send_lease_break(xconn
, new_epoch
, break_flags
,
491 &fsp
->lease
->lease
.lease_key
,
492 break_from
, break_to
);
494 uint8_t smb2_oplock_level
;
495 smb2_oplock_level
= (break_to
& SMB2_LEASE_READ
) ?
496 SMB2_OPLOCK_LEVEL_II
: SMB2_OPLOCK_LEVEL_NONE
;
497 status
= smbd_smb2_send_oplock_break(xconn
,
503 if (!NT_STATUS_IS_OK(status
)) {
504 smbd_server_connection_terminate(xconn
,