/src/samba/source3/locking/locking.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Locking functions |
4 | | Copyright (C) Andrew Tridgell 1992-2000 |
5 | | Copyright (C) Jeremy Allison 1992-2006 |
6 | | Copyright (C) Volker Lendecke 2005 |
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 | | Revision History: |
22 | | |
23 | | 12 aug 96: Erik.Devriendt@te6.siemens.be |
24 | | added support for shared memory implementation of share mode locking |
25 | | |
26 | | May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode |
27 | | locking to deal with multiple share modes per open file. |
28 | | |
29 | | September 1997. Jeremy Allison (jallison@whistle.com). Added oplock |
30 | | support. |
31 | | |
32 | | rewritten completely to use new tdb code. Tridge, Dec '99 |
33 | | |
34 | | Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000. |
35 | | Added Unix Extensions POSIX locking support. Jeremy Allison Mar 2006. |
36 | | */ |
37 | | |
38 | | #include "includes.h" |
39 | | #include "lib/util/time_basic.h" |
40 | | #include "smbd/proto.h" |
41 | | #include "system/filesys.h" |
42 | | #include "lib/util/server_id.h" |
43 | | #include "share_mode_lock.h" |
44 | | #include "share_mode_lock_private.h" |
45 | | #include "locking/proto.h" |
46 | | #include "smbd/globals.h" |
47 | | #include "dbwrap/dbwrap.h" |
48 | | #include "dbwrap/dbwrap_open.h" |
49 | | #include "../libcli/security/security.h" |
50 | | #include "serverid.h" |
51 | | #include "messages.h" |
52 | | #include "util_tdb.h" |
53 | | #include "../librpc/gen_ndr/ndr_open_files.h" |
54 | | #include "librpc/gen_ndr/ndr_file_id.h" |
55 | | #include "librpc/gen_ndr/ndr_leases_db.h" |
56 | | #include "locking/leases_db.h" |
57 | | |
58 | | #undef DBGC_CLASS |
59 | 0 | #define DBGC_CLASS DBGC_LOCKING |
60 | | |
61 | 0 | #define NO_LOCKING_COUNT (-1) |
62 | | |
63 | | /**************************************************************************** |
64 | | Debugging aids :-). |
65 | | ****************************************************************************/ |
66 | | |
67 | | const char *lock_type_name(enum brl_type lock_type) |
68 | 0 | { |
69 | 0 | switch (lock_type) { |
70 | 0 | case READ_LOCK: |
71 | 0 | return "READ"; |
72 | 0 | case WRITE_LOCK: |
73 | 0 | return "WRITE"; |
74 | 0 | default: |
75 | 0 | return "other"; |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | const char *lock_flav_name(enum brl_flavour lock_flav) |
80 | 0 | { |
81 | 0 | return (lock_flav == WINDOWS_LOCK) ? "WINDOWS_LOCK" : "POSIX_LOCK"; |
82 | 0 | } |
83 | | |
84 | | /**************************************************************************** |
85 | | Utility function called to see if a file region is locked. |
86 | | Called in the read/write codepath. |
87 | | ****************************************************************************/ |
88 | | |
89 | | void init_strict_lock_struct(files_struct *fsp, |
90 | | uint64_t smblctx, |
91 | | br_off start, |
92 | | br_off size, |
93 | | enum brl_type lock_type, |
94 | | struct lock_struct *plock) |
95 | 0 | { |
96 | 0 | SMB_ASSERT(lock_type == READ_LOCK || lock_type == WRITE_LOCK); |
97 | | |
98 | 0 | *plock = (struct lock_struct) { |
99 | 0 | .context.smblctx = smblctx, |
100 | 0 | .context.tid = fsp->conn->cnum, |
101 | 0 | .context.pid = messaging_server_id(fsp->conn->sconn->msg_ctx), |
102 | 0 | .start = start, |
103 | 0 | .size = size, |
104 | 0 | .fnum = fsp->fnum, |
105 | 0 | .lock_type = lock_type, |
106 | 0 | .lock_flav = lp_posix_cifsu_locktype(fsp), |
107 | 0 | }; |
108 | 0 | } |
109 | | |
110 | | struct strict_lock_check_state { |
111 | | bool ret; |
112 | | files_struct *fsp; |
113 | | struct lock_struct *plock; |
114 | | }; |
115 | | |
116 | | static void strict_lock_check_default_fn(struct share_mode_lock *lck, |
117 | | struct byte_range_lock *br_lck, |
118 | | void *private_data) |
119 | 0 | { |
120 | 0 | struct strict_lock_check_state *state = private_data; |
121 | | |
122 | | /* |
123 | | * The caller has checked fsp->fsp_flags.can_lock and lp_locking so |
124 | | * br_lck has to be there! |
125 | | */ |
126 | 0 | SMB_ASSERT(br_lck != NULL); |
127 | | |
128 | 0 | state->ret = brl_locktest(br_lck, state->plock, true); |
129 | 0 | } |
130 | | |
131 | | bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) |
132 | 0 | { |
133 | 0 | struct byte_range_lock *br_lck; |
134 | 0 | int strict_locking = lp_strict_locking(fsp->conn->params); |
135 | 0 | NTSTATUS status; |
136 | 0 | bool ret = False; |
137 | |
|
138 | 0 | if (plock->size == 0) { |
139 | 0 | return True; |
140 | 0 | } |
141 | | |
142 | 0 | if (!lp_locking(fsp->conn->params) || |
143 | 0 | !strict_locking || |
144 | 0 | !fsp->fsp_flags.can_lock) |
145 | 0 | { |
146 | 0 | return True; |
147 | 0 | } |
148 | | |
149 | 0 | if (strict_locking == Auto) { |
150 | 0 | uint32_t lease_type = fsp_lease_type(fsp); |
151 | |
|
152 | 0 | if ((lease_type & SMB2_LEASE_READ) && |
153 | 0 | (plock->lock_type == READ_LOCK)) |
154 | 0 | { |
155 | 0 | DBG_DEBUG("optimisation - read lease on file %s\n", |
156 | 0 | fsp_str_dbg(fsp)); |
157 | 0 | return true; |
158 | 0 | } |
159 | | |
160 | 0 | if ((lease_type & SMB2_LEASE_WRITE) && |
161 | 0 | (plock->lock_type == WRITE_LOCK)) |
162 | 0 | { |
163 | 0 | DBG_DEBUG("optimisation - write lease on file %s\n", |
164 | 0 | fsp_str_dbg(fsp)); |
165 | 0 | return true; |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | br_lck = brl_get_locks_readonly(fsp); |
170 | 0 | if (!br_lck) { |
171 | 0 | return true; |
172 | 0 | } |
173 | 0 | ret = brl_locktest(br_lck, plock, false); |
174 | 0 | if (!ret) { |
175 | | /* |
176 | | * We got a lock conflict. Retry with rw locks to enable |
177 | | * autocleanup. This is the slow path anyway. |
178 | | */ |
179 | |
|
180 | 0 | struct strict_lock_check_state state = |
181 | 0 | (struct strict_lock_check_state) { |
182 | 0 | .fsp = fsp, |
183 | 0 | .plock = plock, |
184 | 0 | }; |
185 | |
|
186 | 0 | status = share_mode_do_locked_brl(fsp, |
187 | 0 | strict_lock_check_default_fn, |
188 | 0 | &state); |
189 | 0 | if (!NT_STATUS_IS_OK(status)) { |
190 | 0 | DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n", |
191 | 0 | fsp_str_dbg(fsp), nt_errstr(status)); |
192 | 0 | state.ret = false; |
193 | 0 | } |
194 | 0 | ret = state.ret; |
195 | 0 | } |
196 | |
|
197 | 0 | DBG_DEBUG("flavour = %s brl start=%" PRIu64 " " |
198 | 0 | "len=%" PRIu64 " %s for fnum %" PRIu64 " file %s\n", |
199 | 0 | lock_flav_name(plock->lock_flav), |
200 | 0 | plock->start, |
201 | 0 | plock->size, |
202 | 0 | ret ? "unlocked" : "locked", |
203 | 0 | plock->fnum, |
204 | 0 | fsp_str_dbg(fsp)); |
205 | |
|
206 | 0 | return ret; |
207 | 0 | } |
208 | | |
209 | | /**************************************************************************** |
210 | | Find out if a lock could be granted - return who is blocking us if we can't. |
211 | | ****************************************************************************/ |
212 | | |
213 | | NTSTATUS query_lock(files_struct *fsp, |
214 | | uint64_t *psmblctx, |
215 | | uint64_t *pcount, |
216 | | uint64_t *poffset, |
217 | | enum brl_type *plock_type, |
218 | | enum brl_flavour lock_flav) |
219 | 0 | { |
220 | 0 | struct byte_range_lock *br_lck = NULL; |
221 | |
|
222 | 0 | if (!fsp->fsp_flags.can_lock) { |
223 | 0 | return fsp->fsp_flags.is_directory ? |
224 | 0 | NT_STATUS_INVALID_DEVICE_REQUEST : |
225 | 0 | NT_STATUS_INVALID_HANDLE; |
226 | 0 | } |
227 | | |
228 | 0 | if (!lp_locking(fsp->conn->params)) { |
229 | 0 | return NT_STATUS_OK; |
230 | 0 | } |
231 | | |
232 | 0 | br_lck = brl_get_locks_readonly(fsp); |
233 | 0 | if (!br_lck) { |
234 | 0 | return NT_STATUS_NO_MEMORY; |
235 | 0 | } |
236 | | |
237 | 0 | return brl_lockquery(br_lck, |
238 | 0 | psmblctx, |
239 | 0 | messaging_server_id(fsp->conn->sconn->msg_ctx), |
240 | 0 | poffset, |
241 | 0 | pcount, |
242 | 0 | plock_type, |
243 | 0 | lock_flav); |
244 | 0 | } |
245 | | |
246 | | static void increment_current_lock_count(files_struct *fsp, |
247 | | enum brl_flavour lock_flav) |
248 | 0 | { |
249 | 0 | if (lock_flav == WINDOWS_LOCK && |
250 | 0 | fsp->current_lock_count != NO_LOCKING_COUNT) { |
251 | | /* blocking ie. pending, locks also count here, |
252 | | * as this is an efficiency counter to avoid checking |
253 | | * the lock db. on close. JRA. */ |
254 | |
|
255 | 0 | fsp->current_lock_count++; |
256 | 0 | } else { |
257 | | /* Notice that this has had a POSIX lock request. |
258 | | * We can't count locks after this so forget them. |
259 | | */ |
260 | 0 | fsp->current_lock_count = NO_LOCKING_COUNT; |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | | static void decrement_current_lock_count(files_struct *fsp, |
265 | | enum brl_flavour lock_flav) |
266 | 0 | { |
267 | 0 | if (lock_flav == WINDOWS_LOCK && |
268 | 0 | fsp->current_lock_count != NO_LOCKING_COUNT) { |
269 | 0 | SMB_ASSERT(fsp->current_lock_count > 0); |
270 | 0 | fsp->current_lock_count--; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | /**************************************************************************** |
275 | | Utility function called by locking requests. |
276 | | ****************************************************************************/ |
277 | | |
278 | | NTSTATUS do_lock(struct byte_range_lock *br_lck, |
279 | | TALLOC_CTX *req_mem_ctx, |
280 | | const struct GUID *req_guid, |
281 | | uint64_t smblctx, |
282 | | uint64_t count, |
283 | | uint64_t offset, |
284 | | enum brl_type lock_type, |
285 | | enum brl_flavour lock_flav, |
286 | | struct server_id *pblocker_pid, |
287 | | uint64_t *psmblctx) |
288 | 0 | { |
289 | 0 | files_struct *fsp = brl_fsp(br_lck); |
290 | 0 | struct server_id blocker_pid; |
291 | 0 | uint64_t blocker_smblctx; |
292 | 0 | NTSTATUS status; |
293 | |
|
294 | 0 | SMB_ASSERT(req_mem_ctx != NULL); |
295 | 0 | SMB_ASSERT(req_guid != NULL); |
296 | | |
297 | 0 | if (!fsp->fsp_flags.can_lock) { |
298 | 0 | if (fsp->fsp_flags.is_directory) { |
299 | 0 | return NT_STATUS_INVALID_DEVICE_REQUEST; |
300 | 0 | } |
301 | 0 | return NT_STATUS_INVALID_HANDLE; |
302 | 0 | } |
303 | | |
304 | 0 | if (!lp_locking(fsp->conn->params)) { |
305 | 0 | return NT_STATUS_OK; |
306 | 0 | } |
307 | | |
308 | | /* NOTE! 0 byte long ranges ARE allowed and should be stored */ |
309 | | |
310 | 0 | DBG_DEBUG("lock flavour %s lock type %s start=%"PRIu64" len=%"PRIu64" " |
311 | 0 | "requested for %s file %s\n", |
312 | 0 | lock_flav_name(lock_flav), |
313 | 0 | lock_type_name(lock_type), |
314 | 0 | offset, |
315 | 0 | count, |
316 | 0 | fsp_fnum_dbg(fsp), |
317 | 0 | fsp_str_dbg(fsp)); |
318 | |
|
319 | 0 | brl_req_set(br_lck, req_mem_ctx, req_guid); |
320 | 0 | status = brl_lock(br_lck, |
321 | 0 | smblctx, |
322 | 0 | messaging_server_id(fsp->conn->sconn->msg_ctx), |
323 | 0 | offset, |
324 | 0 | count, |
325 | 0 | lock_type, |
326 | 0 | lock_flav, |
327 | 0 | &blocker_pid, |
328 | 0 | &blocker_smblctx); |
329 | 0 | brl_req_set(br_lck, NULL, NULL); |
330 | 0 | if (!NT_STATUS_IS_OK(status)) { |
331 | 0 | DBG_DEBUG("brl_lock failed: %s\n", nt_errstr(status)); |
332 | 0 | if (psmblctx != NULL) { |
333 | 0 | *psmblctx = blocker_smblctx; |
334 | 0 | } |
335 | 0 | if (pblocker_pid != NULL) { |
336 | 0 | *pblocker_pid = blocker_pid; |
337 | 0 | } |
338 | 0 | return status; |
339 | 0 | } |
340 | | |
341 | 0 | increment_current_lock_count(fsp, lock_flav); |
342 | |
|
343 | 0 | return NT_STATUS_OK; |
344 | 0 | } |
345 | | |
346 | | /**************************************************************************** |
347 | | Utility function called by unlocking requests. |
348 | | ****************************************************************************/ |
349 | | |
350 | | NTSTATUS do_unlock(struct byte_range_lock *br_lck, |
351 | | uint64_t smblctx, |
352 | | uint64_t count, |
353 | | uint64_t offset, |
354 | | enum brl_flavour lock_flav) |
355 | 0 | { |
356 | 0 | files_struct *fsp = brl_fsp(br_lck); |
357 | 0 | bool ok = False; |
358 | |
|
359 | 0 | if (!fsp->fsp_flags.can_lock) { |
360 | 0 | return fsp->fsp_flags.is_directory ? |
361 | 0 | NT_STATUS_INVALID_DEVICE_REQUEST : |
362 | 0 | NT_STATUS_INVALID_HANDLE; |
363 | 0 | } |
364 | | |
365 | 0 | if (!lp_locking(fsp->conn->params)) { |
366 | 0 | return NT_STATUS_OK; |
367 | 0 | } |
368 | | |
369 | 0 | DBG_DEBUG("unlock start=%"PRIu64" len=%"PRIu64" requested for %s file " |
370 | 0 | "%s\n", |
371 | 0 | offset, |
372 | 0 | count, |
373 | 0 | fsp_fnum_dbg(fsp), |
374 | 0 | fsp_str_dbg(fsp)); |
375 | |
|
376 | 0 | ok = brl_unlock(br_lck, |
377 | 0 | smblctx, |
378 | 0 | messaging_server_id(fsp->conn->sconn->msg_ctx), |
379 | 0 | offset, |
380 | 0 | count, |
381 | 0 | lock_flav); |
382 | |
|
383 | 0 | if (!ok) { |
384 | 0 | DEBUG(10,("do_unlock: returning ERRlock.\n" )); |
385 | 0 | return NT_STATUS_RANGE_NOT_LOCKED; |
386 | 0 | } |
387 | | |
388 | 0 | decrement_current_lock_count(fsp, lock_flav); |
389 | 0 | return NT_STATUS_OK; |
390 | 0 | } |
391 | | |
392 | | /**************************************************************************** |
393 | | Remove any locks on this fd. Called from file_close(). |
394 | | ****************************************************************************/ |
395 | | |
396 | | void locking_close_file(files_struct *fsp, |
397 | | enum file_close_type close_type) |
398 | 0 | { |
399 | 0 | struct byte_range_lock *br_lck; |
400 | |
|
401 | 0 | if (!lp_locking(fsp->conn->params)) { |
402 | 0 | return; |
403 | 0 | } |
404 | | |
405 | | /* If we have no outstanding locks or pending |
406 | | * locks then we don't need to look in the lock db. |
407 | | */ |
408 | | |
409 | 0 | if (fsp->current_lock_count == 0) { |
410 | 0 | return; |
411 | 0 | } |
412 | | |
413 | 0 | br_lck = brl_get_locks(talloc_tos(),fsp); |
414 | |
|
415 | 0 | if (br_lck) { |
416 | | /* |
417 | | * Unlocks must trigger dbwrap_watch watchers, |
418 | | * normally in smbd_do_unlocking. Here it's done |
419 | | * implicitly, we're closing the file and thus remove a |
420 | | * share mode. This will wake the waiters. |
421 | | */ |
422 | 0 | brl_close_fnum(br_lck); |
423 | 0 | TALLOC_FREE(br_lck); |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | | /******************************************************************* |
428 | | Print out a share mode. |
429 | | ********************************************************************/ |
430 | | |
431 | | char *share_mode_str(TALLOC_CTX *ctx, int num, |
432 | | const struct file_id *id, |
433 | | const struct share_mode_entry *e) |
434 | 0 | { |
435 | 0 | struct server_id_buf tmp; |
436 | 0 | struct file_id_buf ftmp; |
437 | |
|
438 | 0 | return talloc_asprintf(ctx, "share_mode_entry[%d]: " |
439 | 0 | "pid = %s, share_access = 0x%x, " |
440 | 0 | "access_mask = 0x%x, mid = 0x%llx, type= 0x%x, gen_id = %llu, " |
441 | 0 | "uid = %u, flags = %u, file_id %s, name_hash = 0x%x", |
442 | 0 | num, |
443 | 0 | server_id_str_buf(e->pid, &tmp), |
444 | 0 | e->share_access, |
445 | 0 | e->access_mask, (unsigned long long)e->op_mid, |
446 | 0 | e->op_type, (unsigned long long)e->share_file_id, |
447 | 0 | (unsigned int)e->uid, (unsigned int)e->flags, |
448 | 0 | file_id_str_buf(*id, &ftmp), |
449 | 0 | (unsigned int)e->name_hash); |
450 | 0 | } |
451 | | |
452 | | struct rename_share_filename_state { |
453 | | struct share_mode_data *data; |
454 | | struct messaging_context *msg_ctx; |
455 | | struct server_id self; |
456 | | uint32_t orig_name_hash; |
457 | | uint32_t new_name_hash; |
458 | | struct file_rename_message msg; |
459 | | }; |
460 | | |
461 | | static bool rename_lease_fn(struct share_mode_entry *e, |
462 | | void *private_data) |
463 | 0 | { |
464 | 0 | struct rename_share_filename_state *state = private_data; |
465 | 0 | struct share_mode_data *d = state->data; |
466 | 0 | NTSTATUS status; |
467 | |
|
468 | 0 | status = leases_db_rename(&e->client_guid, |
469 | 0 | &e->lease_key, |
470 | 0 | &d->id, |
471 | 0 | d->servicepath, |
472 | 0 | d->base_name, |
473 | 0 | d->stream_name); |
474 | |
|
475 | 0 | if (!NT_STATUS_IS_OK(status)) { |
476 | | /* Any error recovery possible here ? */ |
477 | 0 | DBG_WARNING("Failed to rename lease key for " |
478 | 0 | "renamed file %s:%s. %s\n", |
479 | 0 | d->base_name, |
480 | 0 | d->stream_name, |
481 | 0 | nt_errstr(status)); |
482 | 0 | } |
483 | |
|
484 | 0 | return false; |
485 | 0 | } |
486 | | |
487 | | /******************************************************************* |
488 | | Sets the service name and filename for rename. |
489 | | At this point we emit "file renamed" messages to all |
490 | | process id's that have this file open. |
491 | | Based on an initial code idea from SATOH Fumiyasu <fumiya@samba.gr.jp> |
492 | | ********************************************************************/ |
493 | | |
494 | | static bool rename_share_filename_fn( |
495 | | struct share_mode_entry *e, |
496 | | bool *modified, |
497 | | void *private_data) |
498 | 0 | { |
499 | 0 | struct rename_share_filename_state *state = private_data; |
500 | 0 | DATA_BLOB blob; |
501 | 0 | enum ndr_err_code ndr_err; |
502 | 0 | bool ok; |
503 | | |
504 | | /* |
505 | | * If this is a hardlink to the inode with a different name, |
506 | | * skip this. |
507 | | */ |
508 | 0 | if (e->name_hash != state->orig_name_hash) { |
509 | 0 | return false; |
510 | 0 | } |
511 | 0 | e->name_hash = state->new_name_hash; |
512 | 0 | *modified = true; |
513 | |
|
514 | 0 | ok = server_id_equal(&e->pid, &state->self); |
515 | 0 | if (ok) { |
516 | 0 | return false; |
517 | 0 | } |
518 | | |
519 | 0 | state->msg.share_file_id = e->share_file_id; |
520 | |
|
521 | 0 | ndr_err = ndr_push_struct_blob( |
522 | 0 | &blob, |
523 | 0 | talloc_tos(), |
524 | 0 | &state->msg, |
525 | 0 | (ndr_push_flags_fn_t)ndr_push_file_rename_message); |
526 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
527 | 0 | DBG_DEBUG("ndr_push_file_rename_message failed: %s\n", |
528 | 0 | ndr_errstr(ndr_err)); |
529 | 0 | return false; |
530 | 0 | } |
531 | 0 | if (DEBUGLEVEL >= 10) { |
532 | 0 | struct server_id_buf tmp; |
533 | 0 | DBG_DEBUG("sending rename message to %s\n", |
534 | 0 | server_id_str_buf(e->pid, &tmp)); |
535 | 0 | NDR_PRINT_DEBUG(file_rename_message, &state->msg); |
536 | 0 | } |
537 | |
|
538 | 0 | messaging_send(state->msg_ctx, e->pid, MSG_SMB_FILE_RENAME, &blob); |
539 | |
|
540 | 0 | TALLOC_FREE(blob.data); |
541 | |
|
542 | 0 | return false; |
543 | 0 | } |
544 | | |
545 | | bool rename_share_filename(struct messaging_context *msg_ctx, |
546 | | struct share_mode_lock *lck, |
547 | | struct file_id id, |
548 | | const char *servicepath, |
549 | | uint32_t orig_name_hash, |
550 | | uint32_t new_name_hash, |
551 | | const struct smb_filename *smb_fname_dst) |
552 | 0 | { |
553 | 0 | struct rename_share_filename_state state = { |
554 | 0 | .msg_ctx = msg_ctx, |
555 | 0 | .self = messaging_server_id(msg_ctx), |
556 | 0 | .orig_name_hash = orig_name_hash, |
557 | 0 | .new_name_hash = new_name_hash, |
558 | 0 | .msg.id = id, |
559 | 0 | .msg.servicepath = servicepath, |
560 | 0 | .msg.base_name = smb_fname_dst->base_name, |
561 | 0 | .msg.stream_name = smb_fname_dst->stream_name, |
562 | 0 | }; |
563 | 0 | struct share_mode_data *d = NULL; |
564 | 0 | NTSTATUS status; |
565 | 0 | bool ok; |
566 | |
|
567 | 0 | DBG_DEBUG("servicepath %s newname %s\n", |
568 | 0 | servicepath, |
569 | 0 | smb_fname_dst->base_name); |
570 | |
|
571 | 0 | status = share_mode_lock_access_private_data(lck, &d); |
572 | 0 | if (!NT_STATUS_IS_OK(status)) { |
573 | | /* Any error recovery possible here ? */ |
574 | 0 | DBG_ERR("share_mode_lock_access_private_data() failed for " |
575 | 0 | "servicepath %s newname %s - %s\n", |
576 | 0 | servicepath, smb_fname_dst->base_name, |
577 | 0 | nt_errstr(status)); |
578 | 0 | return false; |
579 | 0 | } |
580 | 0 | state.data = d; |
581 | | |
582 | | /* |
583 | | * rename_internal_fsp() and rename_internals() add './' to |
584 | | * head of newname if newname does not contain a '/'. |
585 | | */ |
586 | |
|
587 | 0 | if (strncmp(state.msg.base_name, "./", 2) == 0) { |
588 | 0 | state.msg.base_name += 2; |
589 | 0 | } |
590 | |
|
591 | 0 | d->servicepath = talloc_strdup(d, state.msg.servicepath); |
592 | 0 | d->base_name = talloc_strdup(d, state.msg.base_name); |
593 | 0 | d->stream_name = talloc_strdup(d, state.msg.stream_name); |
594 | 0 | if ((d->servicepath == NULL) || |
595 | 0 | (d->base_name == NULL) || |
596 | 0 | ((state.msg.stream_name != NULL) && (d->stream_name == NULL))) { |
597 | 0 | DBG_WARNING("talloc failed\n"); |
598 | 0 | return false; |
599 | 0 | } |
600 | 0 | d->modified = True; |
601 | |
|
602 | 0 | ok = share_mode_forall_entries( |
603 | 0 | lck, rename_share_filename_fn, &state); |
604 | 0 | if (!ok) { |
605 | 0 | DBG_WARNING("share_mode_forall_entries failed\n"); |
606 | 0 | } |
607 | |
|
608 | 0 | ok = share_mode_forall_leases(lck, rename_lease_fn, &state); |
609 | 0 | if (!ok) { |
610 | | /* |
611 | | * Ignore error here. Not sure what to do.. |
612 | | */ |
613 | 0 | DBG_WARNING("share_mode_forall_leases failed\n"); |
614 | 0 | } |
615 | |
|
616 | 0 | return True; |
617 | 0 | } |
618 | | |
619 | | void get_file_infos(struct file_id id, |
620 | | uint32_t name_hash, |
621 | | bool *delete_on_close) |
622 | 0 | { |
623 | 0 | struct share_mode_lock *lck; |
624 | |
|
625 | 0 | if (delete_on_close) { |
626 | 0 | *delete_on_close = false; |
627 | 0 | } |
628 | |
|
629 | 0 | if (!(lck = fetch_share_mode_unlocked(talloc_tos(), id))) { |
630 | 0 | return; |
631 | 0 | } |
632 | | |
633 | 0 | if (delete_on_close) { |
634 | 0 | *delete_on_close = is_delete_on_close_set(lck, name_hash); |
635 | 0 | } |
636 | |
|
637 | 0 | TALLOC_FREE(lck); |
638 | 0 | } |
639 | | |
640 | | bool is_valid_share_mode_entry(const struct share_mode_entry *e) |
641 | 0 | { |
642 | 0 | int num_props = 0; |
643 | |
|
644 | 0 | if (e->stale) { |
645 | 0 | return false; |
646 | 0 | } |
647 | | |
648 | 0 | num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0); |
649 | 0 | num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0); |
650 | 0 | num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0); |
651 | 0 | num_props += (e->op_type == LEASE_OPLOCK); |
652 | |
|
653 | 0 | if ((num_props > 1) && serverid_exists(&e->pid)) { |
654 | 0 | smb_panic("Invalid share mode entry"); |
655 | 0 | } |
656 | 0 | return (num_props != 0); |
657 | 0 | } |
658 | | |
659 | | struct find_lease_ref_state { |
660 | | const struct GUID *client_guid; |
661 | | const struct smb2_lease_key *lease_key; |
662 | | bool found_same; |
663 | | }; |
664 | | |
665 | | static bool find_lease_ref_fn( |
666 | | struct share_mode_entry *e, |
667 | | bool *modified, |
668 | | void *private_data) |
669 | 0 | { |
670 | 0 | struct find_lease_ref_state *state = private_data; |
671 | |
|
672 | 0 | if (e->stale) { |
673 | 0 | return false; |
674 | 0 | } |
675 | 0 | if (e->op_type != LEASE_OPLOCK) { |
676 | 0 | return false; |
677 | 0 | } |
678 | | |
679 | 0 | state->found_same = smb2_lease_equal( |
680 | 0 | &e->client_guid, |
681 | 0 | &e->lease_key, |
682 | 0 | state->client_guid, |
683 | 0 | state->lease_key); |
684 | | /* |
685 | | * If we found a lease reference, look no further (i.e. return true) |
686 | | */ |
687 | 0 | return state->found_same; |
688 | 0 | } |
689 | | |
690 | | NTSTATUS remove_lease_if_stale(struct share_mode_lock *lck, |
691 | | const struct GUID *client_guid, |
692 | | const struct smb2_lease_key *lease_key) |
693 | 0 | { |
694 | 0 | struct find_lease_ref_state state = { |
695 | 0 | .client_guid = client_guid, .lease_key = lease_key, |
696 | 0 | }; |
697 | 0 | struct file_id id = share_mode_lock_file_id(lck); |
698 | 0 | NTSTATUS status; |
699 | 0 | bool ok; |
700 | |
|
701 | 0 | ok = share_mode_forall_entries(lck, find_lease_ref_fn, &state); |
702 | 0 | if (!ok) { |
703 | 0 | DBG_ERR("share_mode_forall_entries failed\n"); |
704 | 0 | return NT_STATUS_INTERNAL_ERROR; |
705 | 0 | } |
706 | | |
707 | 0 | if (state.found_same) { |
708 | 0 | return NT_STATUS_RESOURCE_IN_USE; |
709 | 0 | } |
710 | | |
711 | 0 | status = leases_db_del(client_guid, lease_key, &id); |
712 | 0 | if (!NT_STATUS_IS_OK(status)) { |
713 | 0 | int level = DBGLVL_DEBUG; |
714 | |
|
715 | 0 | if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { |
716 | 0 | level = DBGLVL_ERR; |
717 | 0 | } |
718 | 0 | DBG_PREFIX(level, ("leases_db_del failed: %s\n", |
719 | 0 | nt_errstr(status))); |
720 | 0 | } |
721 | 0 | return status; |
722 | 0 | } |
723 | | |
724 | | bool share_entry_stale_pid(struct share_mode_entry *e) |
725 | 0 | { |
726 | 0 | struct server_id_buf buf; |
727 | 0 | bool exists; |
728 | |
|
729 | 0 | if (e->stale) { |
730 | 0 | return true; |
731 | 0 | } |
732 | | |
733 | 0 | exists = serverid_exists(&e->pid); |
734 | 0 | if (exists) { |
735 | 0 | DBG_DEBUG("PID %s still exists\n", |
736 | 0 | server_id_str_buf(e->pid, &buf)); |
737 | 0 | return false; |
738 | 0 | } |
739 | | |
740 | 0 | DBG_DEBUG("PID %s does not exist anymore\n", |
741 | 0 | server_id_str_buf(e->pid, &buf)); |
742 | |
|
743 | 0 | e->stale = true; |
744 | |
|
745 | 0 | return true; |
746 | 0 | } |
747 | | |
748 | | /**************************************************************************** |
749 | | Adds a delete on close token. |
750 | | ****************************************************************************/ |
751 | | |
752 | | static bool add_delete_on_close_token(struct share_mode_data *d, |
753 | | struct files_struct *fsp, |
754 | | const struct security_token *nt_tok, |
755 | | const struct security_unix_token *tok) |
756 | 0 | { |
757 | 0 | struct delete_token *tmp, *dtl; |
758 | 0 | const struct smb2_lease *lease = NULL; |
759 | |
|
760 | 0 | tmp = talloc_realloc(d, d->delete_tokens, struct delete_token, |
761 | 0 | d->num_delete_tokens+1); |
762 | 0 | if (tmp == NULL) { |
763 | 0 | return false; |
764 | 0 | } |
765 | 0 | d->delete_tokens = tmp; |
766 | 0 | dtl = &d->delete_tokens[d->num_delete_tokens]; |
767 | |
|
768 | 0 | dtl->name_hash = fsp->name_hash; |
769 | |
|
770 | 0 | lease = fsp_get_smb2_lease(fsp); |
771 | 0 | if (lease != NULL) { |
772 | 0 | dtl->parent_lease_key = lease->parent_lease_key; |
773 | 0 | } |
774 | |
|
775 | 0 | dtl->delete_nt_token = security_token_duplicate(d->delete_tokens, nt_tok); |
776 | 0 | if (dtl->delete_nt_token == NULL) { |
777 | 0 | return false; |
778 | 0 | } |
779 | 0 | dtl->delete_token = copy_unix_token(d->delete_tokens, tok); |
780 | 0 | if (dtl->delete_token == NULL) { |
781 | 0 | return false; |
782 | 0 | } |
783 | 0 | d->num_delete_tokens += 1; |
784 | 0 | d->modified = true; |
785 | 0 | return true; |
786 | 0 | } |
787 | | |
788 | | void reset_delete_on_close_lck(files_struct *fsp, |
789 | | struct share_mode_lock *lck) |
790 | 0 | { |
791 | 0 | struct share_mode_data *d = NULL; |
792 | 0 | NTSTATUS status; |
793 | 0 | uint32_t i; |
794 | |
|
795 | 0 | status = share_mode_lock_access_private_data(lck, &d); |
796 | 0 | if (!NT_STATUS_IS_OK(status)) { |
797 | | /* Any error recovery possible here ? */ |
798 | 0 | DBG_ERR("share_mode_lock_access_private_data() failed for " |
799 | 0 | "%s - %s\n", fsp_str_dbg(fsp), nt_errstr(status)); |
800 | 0 | smb_panic(__location__); |
801 | 0 | return; |
802 | 0 | } |
803 | | |
804 | 0 | for (i=0; i<d->num_delete_tokens; i++) { |
805 | 0 | struct delete_token *dt = &d->delete_tokens[i]; |
806 | |
|
807 | 0 | if (dt->name_hash == fsp->name_hash) { |
808 | 0 | d->modified = true; |
809 | | |
810 | | /* Delete this entry. */ |
811 | 0 | TALLOC_FREE(dt->delete_nt_token); |
812 | 0 | TALLOC_FREE(dt->delete_token); |
813 | 0 | *dt = d->delete_tokens[d->num_delete_tokens-1]; |
814 | 0 | d->num_delete_tokens -= 1; |
815 | 0 | } |
816 | 0 | } |
817 | 0 | } |
818 | | |
819 | | struct set_delete_on_close_state { |
820 | | struct messaging_context *msg_ctx; |
821 | | DATA_BLOB blob; |
822 | | }; |
823 | | |
824 | | static bool set_delete_on_close_fn( |
825 | | struct share_mode_entry *e, |
826 | | bool *modified, |
827 | | void *private_data) |
828 | 0 | { |
829 | 0 | struct set_delete_on_close_state *state = private_data; |
830 | 0 | NTSTATUS status; |
831 | |
|
832 | 0 | status = messaging_send( |
833 | 0 | state->msg_ctx, |
834 | 0 | e->pid, |
835 | 0 | MSG_SMB_NOTIFY_CANCEL_DELETED, |
836 | 0 | &state->blob); |
837 | |
|
838 | 0 | if (!NT_STATUS_IS_OK(status)) { |
839 | 0 | struct server_id_buf tmp; |
840 | 0 | DBG_DEBUG("messaging_send to %s returned %s\n", |
841 | 0 | server_id_str_buf(e->pid, &tmp), |
842 | 0 | nt_errstr(status)); |
843 | 0 | } |
844 | |
|
845 | 0 | return false; |
846 | 0 | } |
847 | | |
848 | | /**************************************************************************** |
849 | | Sets the delete on close flag over all share modes on this file. |
850 | | Modify the share mode entry for all files open |
851 | | on this device and inode to tell other smbds we have |
852 | | changed the delete on close flag. This will be noticed |
853 | | in the close code, the last closer will delete the file |
854 | | if flag is set. |
855 | | This makes a copy of any struct security_unix_token into the |
856 | | lck entry. This function is used when the lock is already granted. |
857 | | ****************************************************************************/ |
858 | | |
859 | | void set_delete_on_close_lck(files_struct *fsp, |
860 | | struct share_mode_lock *lck, |
861 | | const struct security_token *nt_tok, |
862 | | const struct security_unix_token *tok) |
863 | 0 | { |
864 | 0 | struct share_mode_data *d = NULL; |
865 | 0 | struct set_delete_on_close_state state = { |
866 | 0 | .msg_ctx = fsp->conn->sconn->msg_ctx |
867 | 0 | }; |
868 | 0 | uint32_t i; |
869 | 0 | bool ret; |
870 | 0 | enum ndr_err_code ndr_err; |
871 | 0 | NTSTATUS status; |
872 | |
|
873 | 0 | status = share_mode_lock_access_private_data(lck, &d); |
874 | 0 | if (!NT_STATUS_IS_OK(status)) { |
875 | | /* Any error recovery possible here ? */ |
876 | 0 | DBG_ERR("share_mode_lock_access_private_data() failed for " |
877 | 0 | "%s - %s\n", fsp_str_dbg(fsp), nt_errstr(status)); |
878 | 0 | smb_panic(__location__); |
879 | 0 | return; |
880 | 0 | } |
881 | | |
882 | 0 | SMB_ASSERT(nt_tok != NULL); |
883 | 0 | SMB_ASSERT(tok != NULL); |
884 | | |
885 | 0 | for (i=0; i<d->num_delete_tokens; i++) { |
886 | 0 | struct delete_token *dt = &d->delete_tokens[i]; |
887 | 0 | if (dt->name_hash == fsp->name_hash) { |
888 | 0 | const struct smb2_lease *lease = NULL; |
889 | |
|
890 | 0 | d->modified = true; |
891 | | |
892 | | /* Replace this token with the given tok. */ |
893 | 0 | ZERO_STRUCT(dt->parent_lease_key); |
894 | 0 | lease = fsp_get_smb2_lease(fsp); |
895 | 0 | if (lease != NULL) { |
896 | 0 | dt->parent_lease_key = lease->parent_lease_key; |
897 | 0 | } |
898 | |
|
899 | 0 | TALLOC_FREE(dt->delete_nt_token); |
900 | 0 | dt->delete_nt_token = security_token_duplicate(dt, nt_tok); |
901 | 0 | SMB_ASSERT(dt->delete_nt_token != NULL); |
902 | 0 | TALLOC_FREE(dt->delete_token); |
903 | 0 | dt->delete_token = copy_unix_token(dt, tok); |
904 | 0 | SMB_ASSERT(dt->delete_token != NULL); |
905 | | |
906 | 0 | return; |
907 | 0 | } |
908 | 0 | } |
909 | | |
910 | 0 | ret = add_delete_on_close_token(d, fsp, nt_tok, tok); |
911 | 0 | SMB_ASSERT(ret); |
912 | | |
913 | 0 | ndr_err = ndr_push_struct_blob( |
914 | 0 | &state.blob, |
915 | 0 | talloc_tos(), |
916 | 0 | &fsp->file_id, |
917 | 0 | (ndr_push_flags_fn_t)ndr_push_file_id); |
918 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
919 | 0 | DBG_ERR("ndr_push_file_id failed: %s\n", |
920 | 0 | ndr_errstr(ndr_err)); |
921 | 0 | smb_panic(__location__); |
922 | 0 | } |
923 | | |
924 | 0 | ret = share_mode_forall_entries( |
925 | 0 | lck, set_delete_on_close_fn, &state); |
926 | 0 | if (!ret) { |
927 | 0 | DBG_ERR("share_mode_forall_entries failed\n"); |
928 | 0 | smb_panic(__location__); |
929 | 0 | } |
930 | | |
931 | 0 | TALLOC_FREE(state.blob.data); |
932 | 0 | } |
933 | | |
934 | | struct set_delete_on_close_locked_state { |
935 | | struct files_struct *fsp; |
936 | | bool delete_on_close; |
937 | | const struct security_token *nt_tok; |
938 | | const struct security_unix_token *tok; |
939 | | }; |
940 | | |
941 | | static void set_delete_on_close_locked(struct share_mode_lock *lck, |
942 | | void *private_data) |
943 | 0 | { |
944 | 0 | struct set_delete_on_close_locked_state *state = |
945 | 0 | (struct set_delete_on_close_locked_state *)private_data; |
946 | |
|
947 | 0 | if (state->delete_on_close) { |
948 | 0 | set_delete_on_close_lck(state->fsp, |
949 | 0 | lck, |
950 | 0 | state->nt_tok, |
951 | 0 | state->tok); |
952 | 0 | } else { |
953 | 0 | reset_delete_on_close_lck(state->fsp, lck); |
954 | 0 | } |
955 | |
|
956 | 0 | state->fsp->fsp_flags.delete_on_close = state->delete_on_close; |
957 | 0 | } |
958 | | |
959 | | bool set_delete_on_close(files_struct *fsp, bool delete_on_close, |
960 | | const struct security_token *nt_tok, |
961 | | const struct security_unix_token *tok) |
962 | 0 | { |
963 | 0 | struct set_delete_on_close_locked_state state = { |
964 | 0 | .fsp = fsp, |
965 | 0 | .delete_on_close = delete_on_close, |
966 | 0 | .nt_tok = nt_tok, |
967 | 0 | .tok = tok, |
968 | 0 | }; |
969 | 0 | NTSTATUS status; |
970 | |
|
971 | 0 | DEBUG(10,("set_delete_on_close: %s delete on close flag for " |
972 | 0 | "%s, file %s\n", |
973 | 0 | delete_on_close ? "Adding" : "Removing", fsp_fnum_dbg(fsp), |
974 | 0 | fsp_str_dbg(fsp))); |
975 | |
|
976 | 0 | if (fsp->fsp_flags.is_directory) { |
977 | 0 | SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); |
978 | 0 | } |
979 | | |
980 | 0 | status = share_mode_do_locked_vfs_denied(fsp->file_id, |
981 | 0 | set_delete_on_close_locked, |
982 | 0 | &state); |
983 | 0 | if (!NT_STATUS_IS_OK(status)) { |
984 | 0 | return false; |
985 | 0 | } |
986 | | |
987 | 0 | return True; |
988 | 0 | } |
989 | | |
990 | | static struct delete_token *find_delete_on_close_token( |
991 | | struct share_mode_data *d, uint32_t name_hash) |
992 | 0 | { |
993 | 0 | uint32_t i; |
994 | |
|
995 | 0 | DBG_DEBUG("name_hash = 0x%"PRIx32"\n", name_hash); |
996 | |
|
997 | 0 | for (i=0; i<d->num_delete_tokens; i++) { |
998 | 0 | struct delete_token *dt = &d->delete_tokens[i]; |
999 | |
|
1000 | 0 | DBG_DEBUG("dt->name_hash = 0x%"PRIx32"\n", |
1001 | 0 | dt->name_hash); |
1002 | 0 | if (dt->name_hash == name_hash) { |
1003 | 0 | return dt; |
1004 | 0 | } |
1005 | 0 | } |
1006 | 0 | return NULL; |
1007 | 0 | } |
1008 | | |
1009 | | /**************************************************************************** |
1010 | | Return the NT token and UNIX token if there's a match. Return true if |
1011 | | found, false if not. |
1012 | | ****************************************************************************/ |
1013 | | |
1014 | | bool get_delete_on_close_token(struct share_mode_lock *lck, |
1015 | | uint32_t name_hash, |
1016 | | const struct security_token **pp_nt_tok, |
1017 | | const struct security_unix_token **pp_tok, |
1018 | | struct smb2_lease_key *parent_lease_key) |
1019 | 0 | { |
1020 | 0 | struct share_mode_data *d = NULL; |
1021 | 0 | struct delete_token *dt; |
1022 | 0 | NTSTATUS status; |
1023 | |
|
1024 | 0 | status = share_mode_lock_access_private_data(lck, &d); |
1025 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1026 | 0 | struct file_id id = share_mode_lock_file_id(lck); |
1027 | 0 | struct file_id_buf id_buf; |
1028 | | /* Any error recovery possible here ? */ |
1029 | 0 | DBG_ERR("share_mode_lock_access_private_data() failed for " |
1030 | 0 | "%s name_hash=%"PRIu32" - %s\n", |
1031 | 0 | file_id_str_buf(id, &id_buf), name_hash, |
1032 | 0 | nt_errstr(status)); |
1033 | 0 | return false; |
1034 | 0 | } |
1035 | | |
1036 | 0 | dt = find_delete_on_close_token(d, name_hash); |
1037 | 0 | if (dt == NULL) { |
1038 | 0 | return false; |
1039 | 0 | } |
1040 | 0 | *pp_nt_tok = dt->delete_nt_token; |
1041 | 0 | *pp_tok = dt->delete_token; |
1042 | 0 | *parent_lease_key = dt->parent_lease_key; |
1043 | 0 | return true; |
1044 | 0 | } |
1045 | | |
1046 | | bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash) |
1047 | 0 | { |
1048 | 0 | struct share_mode_data *d = NULL; |
1049 | 0 | NTSTATUS status; |
1050 | |
|
1051 | 0 | status = share_mode_lock_access_private_data(lck, &d); |
1052 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1053 | 0 | struct file_id id = share_mode_lock_file_id(lck); |
1054 | 0 | struct file_id_buf id_buf; |
1055 | | /* Any error recovery possible here ? */ |
1056 | 0 | DBG_ERR("share_mode_lock_access_private_data() failed for " |
1057 | 0 | "%s name_hash=%"PRIu32" - %s\n", |
1058 | 0 | file_id_str_buf(id, &id_buf), name_hash, |
1059 | 0 | nt_errstr(status)); |
1060 | 0 | return false; |
1061 | 0 | } |
1062 | | |
1063 | 0 | return find_delete_on_close_token(d, name_hash) != NULL; |
1064 | 0 | } |
1065 | | |
1066 | | struct file_has_open_streams_state { |
1067 | | bool found_one; |
1068 | | bool ok; |
1069 | | }; |
1070 | | |
1071 | | static bool file_has_open_streams_fn( |
1072 | | struct share_mode_entry *e, |
1073 | | bool *modified, |
1074 | | void *private_data) |
1075 | 0 | { |
1076 | 0 | struct file_has_open_streams_state *state = private_data; |
1077 | |
|
1078 | 0 | if (!(e->flags & SHARE_ENTRY_FLAG_STREAM_BASEOPEN)) { |
1079 | 0 | return false; |
1080 | 0 | } |
1081 | | |
1082 | 0 | if (share_entry_stale_pid(e)) { |
1083 | 0 | return false; |
1084 | 0 | } |
1085 | | |
1086 | 0 | state->found_one = true; |
1087 | 0 | return true; |
1088 | 0 | } |
1089 | | |
1090 | | static void file_has_open_streams_locked(struct share_mode_lock *lck, |
1091 | | void *private_data) |
1092 | 0 | { |
1093 | 0 | struct file_has_open_streams_state *state = private_data; |
1094 | |
|
1095 | 0 | state->ok = share_mode_forall_entries(lck, |
1096 | 0 | file_has_open_streams_fn, |
1097 | 0 | private_data); |
1098 | 0 | } |
1099 | | |
1100 | | bool file_has_open_streams(files_struct *fsp) |
1101 | 0 | { |
1102 | 0 | struct file_has_open_streams_state state = { .found_one = false }; |
1103 | 0 | NTSTATUS status; |
1104 | |
|
1105 | 0 | status = share_mode_do_locked_vfs_denied(fsp->file_id, |
1106 | 0 | file_has_open_streams_locked, |
1107 | 0 | &state); |
1108 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1109 | 0 | DBG_DEBUG("share_mode_do_locked_vfs_denied() failed - %s\n", |
1110 | 0 | nt_errstr(status)); |
1111 | 0 | return false; |
1112 | 0 | } |
1113 | | |
1114 | 0 | if (!state.ok) { |
1115 | 0 | DBG_DEBUG("share_mode_forall_entries failed\n"); |
1116 | 0 | return false; |
1117 | 0 | } |
1118 | | |
1119 | 0 | return state.found_one; |
1120 | 0 | } |
1121 | | |
1122 | | /* |
1123 | | * Walk share mode entries, looking at every lease only once |
1124 | | */ |
1125 | | |
1126 | | struct share_mode_forall_leases_state { |
1127 | | TALLOC_CTX *mem_ctx; |
1128 | | struct leases_db_key *leases; |
1129 | | bool (*fn)(struct share_mode_entry *e, |
1130 | | void *private_data); |
1131 | | void *private_data; |
1132 | | NTSTATUS status; |
1133 | | }; |
1134 | | |
1135 | | static bool share_mode_forall_leases_fn( |
1136 | | struct share_mode_entry *e, |
1137 | | bool *modified, |
1138 | | void *private_data) |
1139 | 0 | { |
1140 | 0 | struct share_mode_forall_leases_state *state = private_data; |
1141 | 0 | struct leases_db_key *leases = state->leases; |
1142 | 0 | size_t i, num_leases; |
1143 | 0 | bool stop; |
1144 | |
|
1145 | 0 | if (e->op_type != LEASE_OPLOCK) { |
1146 | 0 | return false; |
1147 | 0 | } |
1148 | | |
1149 | 0 | num_leases = talloc_array_length(leases); |
1150 | |
|
1151 | 0 | for (i=0; i<num_leases; i++) { |
1152 | 0 | struct leases_db_key *l = &leases[i]; |
1153 | 0 | bool same = smb2_lease_equal( |
1154 | 0 | &e->client_guid, |
1155 | 0 | &e->lease_key, |
1156 | 0 | &l->client_guid, |
1157 | 0 | &l->lease_key); |
1158 | 0 | if (same) { |
1159 | 0 | return false; |
1160 | 0 | } |
1161 | 0 | } |
1162 | | |
1163 | 0 | leases = talloc_realloc( |
1164 | 0 | state->mem_ctx, |
1165 | 0 | leases, |
1166 | 0 | struct leases_db_key, |
1167 | 0 | num_leases+1); |
1168 | 0 | if (leases == NULL) { |
1169 | 0 | state->status = NT_STATUS_NO_MEMORY; |
1170 | 0 | return true; |
1171 | 0 | } |
1172 | 0 | leases[num_leases] = (struct leases_db_key) { |
1173 | 0 | .client_guid = e->client_guid, |
1174 | 0 | .lease_key = e->lease_key, |
1175 | 0 | }; |
1176 | 0 | state->leases = leases; |
1177 | |
|
1178 | 0 | stop = state->fn(e, state->private_data); |
1179 | 0 | return stop; |
1180 | 0 | } |
1181 | | |
1182 | | bool share_mode_forall_leases( |
1183 | | struct share_mode_lock *lck, |
1184 | | bool (*fn)(struct share_mode_entry *e, |
1185 | | void *private_data), |
1186 | | void *private_data) |
1187 | 0 | { |
1188 | 0 | struct share_mode_forall_leases_state state = { |
1189 | 0 | .mem_ctx = talloc_tos(), |
1190 | 0 | .fn = fn, |
1191 | 0 | .private_data = private_data |
1192 | 0 | }; |
1193 | 0 | bool ok; |
1194 | |
|
1195 | 0 | ok = share_mode_forall_entries( |
1196 | 0 | lck, share_mode_forall_leases_fn, &state); |
1197 | 0 | TALLOC_FREE(state.leases); |
1198 | 0 | if (!ok) { |
1199 | 0 | DBG_ERR("share_mode_forall_entries failed\n"); |
1200 | 0 | return false; |
1201 | 0 | } |
1202 | | |
1203 | 0 | if (!NT_STATUS_IS_OK(state.status)) { |
1204 | 0 | DBG_ERR("share_mode_forall_leases_fn returned %s\n", |
1205 | 0 | nt_errstr(state.status)); |
1206 | 0 | return false; |
1207 | 0 | } |
1208 | | |
1209 | 0 | return true; |
1210 | 0 | } |