/src/samba/source3/smbd/smb2_break.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Core SMB2 server |
4 | | |
5 | | Copyright (C) Stefan Metzmacher 2009 |
6 | | Copyright (C) Jeremy Allison 2010 |
7 | | |
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. |
12 | | |
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. |
17 | | |
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/>. |
20 | | */ |
21 | | |
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" |
28 | | |
29 | | #undef DBGC_CLASS |
30 | 0 | #define DBGC_CLASS DBGC_SMB2 |
31 | | |
32 | | static NTSTATUS smbd_smb2_request_process_lease_break( |
33 | | struct smbd_smb2_request *req); |
34 | | |
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); |
42 | | |
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) |
45 | 0 | { |
46 | 0 | NTSTATUS status; |
47 | 0 | const uint8_t *inbody; |
48 | 0 | uint8_t in_oplock_level; |
49 | 0 | uint64_t in_file_id_persistent; |
50 | 0 | uint64_t in_file_id_volatile; |
51 | 0 | struct files_struct *in_fsp; |
52 | 0 | struct tevent_req *subreq; |
53 | |
|
54 | 0 | status = smbd_smb2_request_verify_sizes(req, 0x18); |
55 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { |
56 | | /* |
57 | | * Retry as a lease break |
58 | | */ |
59 | 0 | return smbd_smb2_request_process_lease_break(req); |
60 | 0 | } |
61 | 0 | if (!NT_STATUS_IS_OK(status)) { |
62 | 0 | return smbd_smb2_request_error(req, status); |
63 | 0 | } |
64 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
65 | |
|
66 | 0 | in_oplock_level = CVAL(inbody, 0x02); |
67 | | |
68 | | /* 0x03 1 bytes reserved */ |
69 | | /* 0x04 4 bytes reserved */ |
70 | 0 | in_file_id_persistent = BVAL(inbody, 0x08); |
71 | 0 | in_file_id_volatile = BVAL(inbody, 0x10); |
72 | |
|
73 | 0 | in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); |
74 | 0 | if (in_fsp == NULL) { |
75 | 0 | return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); |
76 | 0 | } |
77 | | |
78 | | /* Are we awaiting a break message ? */ |
79 | 0 | if (in_fsp->oplock_timeout == NULL) { |
80 | 0 | return smbd_smb2_request_error( |
81 | 0 | req, NT_STATUS_INVALID_OPLOCK_PROTOCOL); |
82 | 0 | } |
83 | | |
84 | 0 | if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE && |
85 | 0 | in_oplock_level != SMB2_OPLOCK_LEVEL_II) { |
86 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
87 | 0 | } |
88 | | |
89 | 0 | subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx, |
90 | 0 | req, in_fsp, in_oplock_level); |
91 | 0 | if (subreq == NULL) { |
92 | 0 | return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
93 | 0 | } |
94 | 0 | tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req); |
95 | |
|
96 | 0 | return smbd_smb2_request_pending_queue(req, subreq, 500); |
97 | 0 | } |
98 | | |
99 | | static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq) |
100 | 0 | { |
101 | 0 | struct smbd_smb2_request *req = tevent_req_callback_data(subreq, |
102 | 0 | struct smbd_smb2_request); |
103 | 0 | const uint8_t *inbody; |
104 | 0 | uint64_t in_file_id_persistent; |
105 | 0 | uint64_t in_file_id_volatile; |
106 | 0 | uint8_t out_oplock_level = 0; |
107 | 0 | DATA_BLOB outbody; |
108 | 0 | NTSTATUS status; |
109 | 0 | NTSTATUS error; /* transport error */ |
110 | |
|
111 | 0 | status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level); |
112 | 0 | TALLOC_FREE(subreq); |
113 | 0 | if (!NT_STATUS_IS_OK(status)) { |
114 | 0 | error = smbd_smb2_request_error(req, status); |
115 | 0 | if (!NT_STATUS_IS_OK(error)) { |
116 | 0 | smbd_server_connection_terminate(req->xconn, |
117 | 0 | nt_errstr(error)); |
118 | 0 | return; |
119 | 0 | } |
120 | 0 | return; |
121 | 0 | } |
122 | | |
123 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
124 | |
|
125 | 0 | in_file_id_persistent = BVAL(inbody, 0x08); |
126 | 0 | in_file_id_volatile = BVAL(inbody, 0x10); |
127 | |
|
128 | 0 | outbody = smbd_smb2_generate_outbody(req, 0x18); |
129 | 0 | if (outbody.data == NULL) { |
130 | 0 | error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
131 | 0 | if (!NT_STATUS_IS_OK(error)) { |
132 | 0 | smbd_server_connection_terminate(req->xconn, |
133 | 0 | nt_errstr(error)); |
134 | 0 | return; |
135 | 0 | } |
136 | 0 | return; |
137 | 0 | } |
138 | | |
139 | 0 | SSVAL(outbody.data, 0x00, 0x18); /* struct size */ |
140 | 0 | SCVAL(outbody.data, 0x02, |
141 | 0 | out_oplock_level); /* SMB2 oplock level */ |
142 | 0 | SCVAL(outbody.data, 0x03, 0); /* reserved */ |
143 | 0 | SIVAL(outbody.data, 0x04, 0); /* reserved */ |
144 | 0 | SBVAL(outbody.data, 0x08, |
145 | 0 | in_file_id_persistent); /* file id (persistent) */ |
146 | 0 | SBVAL(outbody.data, 0x10, |
147 | 0 | in_file_id_volatile); /* file id (volatile) */ |
148 | |
|
149 | 0 | error = smbd_smb2_request_done(req, outbody, NULL); |
150 | 0 | if (!NT_STATUS_IS_OK(error)) { |
151 | 0 | smbd_server_connection_terminate(req->xconn, |
152 | 0 | nt_errstr(error)); |
153 | 0 | return; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | struct smbd_smb2_oplock_break_state { |
158 | | struct smbd_smb2_request *smb2req; |
159 | | uint8_t out_oplock_level; /* SMB2 oplock level. */ |
160 | | }; |
161 | | |
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) |
167 | 0 | { |
168 | 0 | struct tevent_req *req; |
169 | 0 | struct smbd_smb2_oplock_break_state *state; |
170 | 0 | struct smb_request *smbreq; |
171 | 0 | int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level); |
172 | 0 | bool break_to_none = (oplocklevel == NO_OPLOCK); |
173 | 0 | bool result; |
174 | |
|
175 | 0 | req = tevent_req_create(mem_ctx, &state, |
176 | 0 | struct smbd_smb2_oplock_break_state); |
177 | 0 | if (req == NULL) { |
178 | 0 | return NULL; |
179 | 0 | } |
180 | 0 | state->smb2req = smb2req; |
181 | 0 | state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE; |
182 | |
|
183 | 0 | DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, " |
184 | 0 | "samba level %d\n", |
185 | 0 | fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), |
186 | 0 | oplocklevel)); |
187 | |
|
188 | 0 | smbreq = smbd_smb2_fake_smb_request(smb2req, fsp); |
189 | 0 | if (tevent_req_nomem(smbreq, req)) { |
190 | 0 | return tevent_req_post(req, ev); |
191 | 0 | } |
192 | | |
193 | 0 | DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client " |
194 | 0 | "for file %s, %s\n", |
195 | 0 | (unsigned int)in_oplock_level, |
196 | 0 | fsp_str_dbg(fsp), |
197 | 0 | fsp_fnum_dbg(fsp))); |
198 | |
|
199 | 0 | if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) || |
200 | 0 | (break_to_none)) { |
201 | 0 | result = remove_oplock(fsp); |
202 | 0 | state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE; |
203 | 0 | } else { |
204 | 0 | result = downgrade_oplock(fsp); |
205 | 0 | state->out_oplock_level = SMB2_OPLOCK_LEVEL_II; |
206 | 0 | } |
207 | |
|
208 | 0 | if (!result) { |
209 | 0 | DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing " |
210 | 0 | "oplock on file %s\n", fsp_str_dbg(fsp))); |
211 | | /* Hmmm. Is this panic justified? */ |
212 | 0 | smb_panic("internal tdb error"); |
213 | 0 | } |
214 | | |
215 | 0 | tevent_req_done(req); |
216 | 0 | return tevent_req_post(req, ev); |
217 | 0 | } |
218 | | |
219 | | static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req, |
220 | | uint8_t *out_oplock_level) |
221 | 0 | { |
222 | 0 | NTSTATUS status; |
223 | 0 | struct smbd_smb2_oplock_break_state *state = |
224 | 0 | tevent_req_data(req, |
225 | 0 | struct smbd_smb2_oplock_break_state); |
226 | |
|
227 | 0 | if (tevent_req_is_nterror(req, &status)) { |
228 | 0 | tevent_req_received(req); |
229 | 0 | return status; |
230 | 0 | } |
231 | | |
232 | 0 | *out_oplock_level = state->out_oplock_level; |
233 | |
|
234 | 0 | tevent_req_received(req); |
235 | 0 | return NT_STATUS_OK; |
236 | 0 | } |
237 | | |
238 | | static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq); |
239 | | |
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); |
246 | | |
247 | | |
248 | | static NTSTATUS smbd_smb2_request_process_lease_break( |
249 | | struct smbd_smb2_request *req) |
250 | 0 | { |
251 | 0 | NTSTATUS status; |
252 | 0 | const uint8_t *inbody; |
253 | 0 | struct smb2_lease_key in_lease_key; |
254 | 0 | uint32_t in_lease_state; |
255 | 0 | struct tevent_req *subreq; |
256 | |
|
257 | 0 | status = smbd_smb2_request_verify_sizes(req, 0x24); |
258 | 0 | if (!NT_STATUS_IS_OK(status)) { |
259 | 0 | return smbd_smb2_request_error(req, status); |
260 | 0 | } |
261 | | |
262 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
263 | |
|
264 | 0 | in_lease_key.data[0] = BVAL(inbody, 8); |
265 | 0 | in_lease_key.data[1] = BVAL(inbody, 16); |
266 | 0 | in_lease_state = IVAL(inbody, 24); |
267 | |
|
268 | 0 | subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req, |
269 | 0 | in_lease_key, in_lease_state); |
270 | 0 | if (subreq == NULL) { |
271 | 0 | return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
272 | 0 | } |
273 | 0 | tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req); |
274 | |
|
275 | 0 | return smbd_smb2_request_pending_queue(req, subreq, 500); |
276 | 0 | } |
277 | | |
278 | | static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq) |
279 | 0 | { |
280 | 0 | struct smbd_smb2_request *req = tevent_req_callback_data( |
281 | 0 | subreq, struct smbd_smb2_request); |
282 | 0 | const uint8_t *inbody; |
283 | 0 | struct smb2_lease_key in_lease_key; |
284 | 0 | uint32_t out_lease_state = 0; |
285 | 0 | DATA_BLOB outbody; |
286 | 0 | NTSTATUS status; |
287 | 0 | NTSTATUS error; /* transport error */ |
288 | |
|
289 | 0 | status = smbd_smb2_lease_break_recv(subreq, &out_lease_state); |
290 | 0 | TALLOC_FREE(subreq); |
291 | 0 | if (!NT_STATUS_IS_OK(status)) { |
292 | 0 | error = smbd_smb2_request_error(req, status); |
293 | 0 | if (!NT_STATUS_IS_OK(error)) { |
294 | 0 | smbd_server_connection_terminate(req->xconn, |
295 | 0 | nt_errstr(error)); |
296 | 0 | return; |
297 | 0 | } |
298 | 0 | return; |
299 | 0 | } |
300 | | |
301 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
302 | |
|
303 | 0 | in_lease_key.data[0] = BVAL(inbody, 8); |
304 | 0 | in_lease_key.data[1] = BVAL(inbody, 16); |
305 | |
|
306 | 0 | outbody = smbd_smb2_generate_outbody(req, 0x24); |
307 | 0 | if (outbody.data == NULL) { |
308 | 0 | error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
309 | 0 | if (!NT_STATUS_IS_OK(error)) { |
310 | 0 | smbd_server_connection_terminate(req->xconn, |
311 | 0 | nt_errstr(error)); |
312 | 0 | return; |
313 | 0 | } |
314 | 0 | return; |
315 | 0 | } |
316 | | |
317 | 0 | SSVAL(outbody.data, 0x00, 0x24); /* struct size */ |
318 | 0 | SSVAL(outbody.data, 0x02, 0); /* reserved */ |
319 | 0 | SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */ |
320 | 0 | SBVAL(outbody.data, 0x08, in_lease_key.data[0]); |
321 | 0 | SBVAL(outbody.data, 0x10, in_lease_key.data[1]); |
322 | 0 | SIVAL(outbody.data, 0x18, out_lease_state); |
323 | 0 | SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */ |
324 | |
|
325 | 0 | error = smbd_smb2_request_done(req, outbody, NULL); |
326 | 0 | if (!NT_STATUS_IS_OK(error)) { |
327 | 0 | smbd_server_connection_terminate(req->xconn, |
328 | 0 | nt_errstr(error)); |
329 | 0 | return; |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | | struct smbd_smb2_lease_break_state { |
334 | | uint32_t lease_state; |
335 | | }; |
336 | | |
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; |
343 | | }; |
344 | | |
345 | | static void lease_parser( |
346 | | uint32_t num_files, |
347 | | const struct leases_db_file *files, |
348 | | void *private_data) |
349 | 0 | { |
350 | 0 | struct lease_lookup_state *lls = |
351 | 0 | (struct lease_lookup_state *)private_data; |
352 | |
|
353 | 0 | lls->status = NT_STATUS_OK; |
354 | 0 | lls->num_file_ids = num_files; |
355 | 0 | lls->status = leases_db_copy_file_ids(lls->mem_ctx, |
356 | 0 | num_files, |
357 | 0 | files, |
358 | 0 | &lls->ids); |
359 | 0 | } |
360 | | |
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) |
365 | 0 | { |
366 | 0 | struct tevent_req *req; |
367 | 0 | struct smbd_smb2_lease_break_state *state; |
368 | 0 | struct lease_lookup_state lls = {.mem_ctx = mem_ctx}; |
369 | 0 | NTSTATUS status; |
370 | |
|
371 | 0 | req = tevent_req_create(mem_ctx, &state, |
372 | 0 | struct smbd_smb2_lease_break_state); |
373 | 0 | if (req == NULL) { |
374 | 0 | return NULL; |
375 | 0 | } |
376 | 0 | state->lease_state = in_lease_state; |
377 | | |
378 | | /* Find any file ids with this lease key. */ |
379 | 0 | status = leases_db_parse(&smb2_req->xconn->smb2.client.guid, |
380 | 0 | &in_lease_key, |
381 | 0 | lease_parser, |
382 | 0 | &lls); |
383 | |
|
384 | 0 | if (!NT_STATUS_IS_OK(status)) { |
385 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { |
386 | 0 | status = NT_STATUS_OBJECT_NAME_NOT_FOUND; |
387 | 0 | DEBUG(10, ("No record for lease key found\n")); |
388 | 0 | } |
389 | 0 | tevent_req_nterror(req, status); |
390 | 0 | return tevent_req_post(req, ev); |
391 | 0 | } |
392 | | |
393 | 0 | if (tevent_req_nterror(req, lls.status)) { |
394 | 0 | return tevent_req_post(req, ev); |
395 | 0 | } |
396 | | |
397 | 0 | if (lls.num_file_ids == 0) { |
398 | 0 | tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); |
399 | 0 | return tevent_req_post(req, ev); |
400 | 0 | } |
401 | | |
402 | 0 | status = downgrade_lease(smb2_req->xconn->client, |
403 | 0 | lls.num_file_ids, |
404 | 0 | lls.ids, |
405 | 0 | &in_lease_key, |
406 | 0 | in_lease_state); |
407 | |
|
408 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) { |
409 | 0 | tevent_req_done(req); |
410 | 0 | return tevent_req_post(req, ev); |
411 | 0 | } |
412 | 0 | if (tevent_req_nterror(req, status)) { |
413 | 0 | DEBUG(10, ("downgrade_lease returned %s\n", |
414 | 0 | nt_errstr(status))); |
415 | 0 | return tevent_req_post(req, ev); |
416 | 0 | } |
417 | | |
418 | 0 | tevent_req_done(req); |
419 | 0 | return tevent_req_post(req, ev); |
420 | 0 | } |
421 | | |
422 | | static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req, |
423 | | uint32_t *out_lease_state) |
424 | 0 | { |
425 | 0 | struct smbd_smb2_lease_break_state *state = tevent_req_data( |
426 | 0 | req, struct smbd_smb2_lease_break_state); |
427 | 0 | NTSTATUS status; |
428 | |
|
429 | 0 | if (tevent_req_is_nterror(req, &status)) { |
430 | 0 | return status; |
431 | 0 | } |
432 | 0 | *out_lease_state = state->lease_state; |
433 | 0 | return NT_STATUS_OK; |
434 | 0 | } |
435 | | |
436 | | /********************************************************* |
437 | | Create and send an asynchronous |
438 | | SMB2 OPLOCK_BREAK_NOTIFICATION. |
439 | | *********************************************************/ |
440 | | |
441 | | void send_break_message_smb2(files_struct *fsp, |
442 | | uint32_t break_from, |
443 | | uint32_t break_to) |
444 | 0 | { |
445 | 0 | struct smbXsrv_client *client = |
446 | 0 | fsp->conn->sconn->client; |
447 | 0 | NTSTATUS status; |
448 | |
|
449 | 0 | if (!NT_STATUS_IS_OK(fsp->op->status)) { |
450 | 0 | DBG_DEBUG("skip oplock break for file %s, %s, " |
451 | 0 | "smb2 level %u fsp status=%s\n", |
452 | 0 | fsp_str_dbg(fsp), |
453 | 0 | fsp_fnum_dbg(fsp), |
454 | 0 | (unsigned int)break_to, |
455 | 0 | nt_errstr(fsp->op->status)); |
456 | 0 | return; |
457 | 0 | } |
458 | | |
459 | 0 | DBG_DEBUG("sending oplock break " |
460 | 0 | "for file %s, %s, smb2 level %u\n", |
461 | 0 | fsp_str_dbg(fsp), |
462 | 0 | fsp_fnum_dbg(fsp), |
463 | 0 | (unsigned int)break_to); |
464 | |
|
465 | 0 | if (fsp->oplock_type == LEASE_OPLOCK) { |
466 | 0 | uint32_t break_flags = 0; |
467 | 0 | uint16_t new_epoch; |
468 | |
|
469 | 0 | if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) { |
470 | 0 | break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; |
471 | 0 | } |
472 | |
|
473 | 0 | if (fsp->lease->lease.lease_version > 1) { |
474 | 0 | new_epoch = fsp->lease->lease.lease_epoch; |
475 | 0 | } else { |
476 | 0 | new_epoch = 0; |
477 | 0 | } |
478 | |
|
479 | 0 | status = smbd_smb2_send_lease_break(client, new_epoch, break_flags, |
480 | 0 | &fsp->lease->lease.lease_key, |
481 | 0 | break_from, break_to); |
482 | 0 | } else { |
483 | 0 | uint8_t smb2_oplock_level; |
484 | 0 | smb2_oplock_level = (break_to & SMB2_LEASE_READ) ? |
485 | 0 | SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE; |
486 | 0 | status = smbd_smb2_send_oplock_break(client, |
487 | 0 | fsp->op, |
488 | 0 | smb2_oplock_level); |
489 | 0 | } |
490 | 0 | if (!NT_STATUS_IS_OK(status)) { |
491 | | smbd_server_disconnect_client(client, |
492 | 0 | nt_errstr(status)); |
493 | 0 | return; |
494 | 0 | } |
495 | 0 | } |