lib: Fix a shutdown crash with "clustering = yes"
[Samba.git] / source3 / locking / leases_db.c
bloba12b421d26034f5ad9461232d1723f06fdc1d486
1 /*
2 Unix SMB/CIFS implementation.
3 Map lease keys to file ids
4 Copyright (C) Volker Lendecke 2013
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "locking/leases_db.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
26 #include "util_tdb.h"
27 #include "ndr.h"
28 #include "librpc/gen_ndr/ndr_leases_db.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_LOCKING
33 /* the leases database handle */
34 static struct db_context *leases_db;
36 bool leases_db_init(bool read_only)
38 char *db_path;
40 if (leases_db) {
41 return true;
44 db_path = lock_path(talloc_tos(), "leases.tdb");
45 if (db_path == NULL) {
46 return false;
49 leases_db = db_open(NULL, db_path, 0,
50 TDB_DEFAULT|
51 TDB_VOLATILE|
52 TDB_CLEAR_IF_FIRST|
53 TDB_SEQNUM|
54 TDB_INCOMPATIBLE_HASH,
55 read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
56 DBWRAP_LOCK_ORDER_4, DBWRAP_FLAG_NONE);
57 TALLOC_FREE(db_path);
58 if (leases_db == NULL) {
59 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
60 return false;
63 return true;
66 struct leases_db_key_buf {
67 uint8_t buf[32];
70 static TDB_DATA leases_db_key(struct leases_db_key_buf *buf,
71 const struct GUID *client_guid,
72 const struct smb2_lease_key *lease_key)
74 struct leases_db_key db_key = {
75 .client_guid = *client_guid,
76 .lease_key = *lease_key };
77 DATA_BLOB blob = { .data = buf->buf, .length = sizeof(buf->buf) };
78 enum ndr_err_code ndr_err;
80 if (DEBUGLEVEL >= 10) {
81 DBG_DEBUG("\n");
82 NDR_PRINT_DEBUG(leases_db_key, &db_key);
85 ndr_err = ndr_push_struct_into_fixed_blob(
86 &blob, &db_key, (ndr_push_flags_fn_t)ndr_push_leases_db_key);
87 SMB_ASSERT(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
89 return (TDB_DATA) { .dptr = buf->buf, .dsize = sizeof(buf->buf) };
92 struct leases_db_do_locked_state {
93 void (*fn)(struct leases_db_value *value,
94 bool *modified,
95 void *private_data);
96 void *private_data;
97 NTSTATUS status;
100 static void leases_db_do_locked_fn(
101 struct db_record *rec,
102 TDB_DATA db_value,
103 void *private_data)
105 struct leases_db_do_locked_state *state = private_data;
106 DATA_BLOB blob = { .data = db_value.dptr, .length = db_value.dsize };
107 struct leases_db_value *value = NULL;
108 enum ndr_err_code ndr_err;
109 bool modified = false;
111 value = talloc_zero(talloc_tos(), struct leases_db_value);
112 if (value == NULL) {
113 state->status = NT_STATUS_NO_MEMORY;
114 goto done;
117 if (blob.length != 0) {
118 ndr_err = ndr_pull_struct_blob_all(
119 &blob,
120 value,
121 value,
122 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DBG_DEBUG("ndr_pull_struct_blob_failed: %s\n",
125 ndr_errstr(ndr_err));
126 state->status = ndr_map_error2ntstatus(ndr_err);
127 goto done;
131 state->fn(value, &modified, state->private_data);
133 if (!modified) {
134 goto done;
137 if (value->num_files == 0) {
138 state->status = dbwrap_record_delete(rec);
139 if (!NT_STATUS_IS_OK(state->status)) {
140 DBG_DEBUG("dbwrap_record_delete returned %s\n",
141 nt_errstr(state->status));
143 goto done;
146 ndr_err = ndr_push_struct_blob(
147 &blob,
148 value,
149 value,
150 (ndr_push_flags_fn_t)ndr_push_leases_db_value);
151 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
152 DBG_DEBUG("ndr_push_struct_blob_failed: %s\n",
153 ndr_errstr(ndr_err));
154 state->status = ndr_map_error2ntstatus(ndr_err);
155 goto done;
158 if (DEBUGLEVEL >= 10) {
159 DBG_DEBUG("\n");
160 NDR_PRINT_DEBUG(leases_db_value, value);
163 db_value = make_tdb_data(blob.data, blob.length);
165 state->status = dbwrap_record_store(rec, db_value, 0);
166 if (!NT_STATUS_IS_OK(state->status)) {
167 DBG_DEBUG("dbwrap_record_store returned %s\n",
168 nt_errstr(state->status));
171 done:
172 TALLOC_FREE(value);
175 static NTSTATUS leases_db_do_locked(
176 const struct GUID *client_guid,
177 const struct smb2_lease_key *lease_key,
178 void (*fn)(struct leases_db_value *value,
179 bool *modified,
180 void *private_data),
181 void *private_data)
183 struct leases_db_key_buf keybuf;
184 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
185 struct leases_db_do_locked_state state = {
186 .fn = fn, .private_data = private_data,
188 NTSTATUS status;
190 if (!leases_db_init(false)) {
191 return NT_STATUS_INTERNAL_ERROR;
194 status = dbwrap_do_locked(
195 leases_db, db_key, leases_db_do_locked_fn, &state);
196 if (!NT_STATUS_IS_OK(status)) {
197 return status;
199 return state.status;
202 struct leases_db_add_state {
203 const struct file_id *id;
204 uint32_t current_state;
205 uint16_t lease_version;
206 uint16_t epoch;
207 const char *servicepath;
208 const char *base_name;
209 const char *stream_name;
210 NTSTATUS status;
213 static void leases_db_add_fn(
214 struct leases_db_value *value, bool *modified, void *private_data)
216 struct leases_db_add_state *state = private_data;
217 struct leases_db_file *tmp = NULL;
218 uint32_t i;
220 /* id must be unique. */
221 for (i = 0; i < value->num_files; i++) {
222 if (file_id_equal(state->id, &value->files[i].id)) {
223 state->status = NT_STATUS_OBJECT_NAME_COLLISION;
224 return;
228 if (value->num_files == 0) {
229 /* new record */
230 value->current_state = state->current_state;
231 value->lease_version = state->lease_version;
232 value->epoch = state->epoch;
235 tmp = talloc_realloc(
236 value,
237 value->files,
238 struct leases_db_file,
239 value->num_files + 1);
240 if (tmp == NULL) {
241 state->status = NT_STATUS_NO_MEMORY;
242 return;
244 value->files = tmp;
246 value->files[value->num_files] = (struct leases_db_file) {
247 .id = *state->id,
248 .servicepath = state->servicepath,
249 .base_name = state->base_name,
250 .stream_name = state->stream_name,
252 value->num_files += 1;
254 *modified = true;
257 NTSTATUS leases_db_add(const struct GUID *client_guid,
258 const struct smb2_lease_key *lease_key,
259 const struct file_id *id,
260 uint32_t current_state,
261 uint16_t lease_version,
262 uint16_t epoch,
263 const char *servicepath,
264 const char *base_name,
265 const char *stream_name)
267 struct leases_db_add_state state = {
268 .id = id,
269 .current_state = current_state,
270 .lease_version = lease_version,
271 .epoch = epoch,
272 .servicepath = servicepath,
273 .base_name = base_name,
274 .stream_name = stream_name,
276 NTSTATUS status;
278 status = leases_db_do_locked(
279 client_guid, lease_key, leases_db_add_fn, &state);
280 if (!NT_STATUS_IS_OK(status)) {
281 DBG_DEBUG("leases_db_do_locked failed: %s\n",
282 nt_errstr(status));
283 return status;
285 return state.status;
288 struct leases_db_del_state {
289 const struct file_id *id;
290 NTSTATUS status;
293 static void leases_db_del_fn(
294 struct leases_db_value *value, bool *modified, void *private_data)
296 struct leases_db_del_state *state = private_data;
297 uint32_t i;
299 for (i = 0; i < value->num_files; i++) {
300 if (file_id_equal(state->id, &value->files[i].id)) {
301 break;
304 if (i == value->num_files) {
305 state->status = NT_STATUS_NOT_FOUND;
306 return;
309 value->files[i] = value->files[value->num_files-1];
310 value->num_files -= 1;
312 *modified = true;
315 NTSTATUS leases_db_del(const struct GUID *client_guid,
316 const struct smb2_lease_key *lease_key,
317 const struct file_id *id)
319 struct leases_db_del_state state = { .id = id };
320 NTSTATUS status;
322 status = leases_db_do_locked(
323 client_guid, lease_key, leases_db_del_fn, &state);
324 if (!NT_STATUS_IS_OK(status)) {
325 DBG_DEBUG("leases_db_do_locked failed: %s\n",
326 nt_errstr(status));
327 return status;
329 return state.status;
332 struct leases_db_fetch_state {
333 void (*parser)(uint32_t num_files,
334 const struct leases_db_file *files,
335 void *private_data);
336 void *private_data;
337 NTSTATUS status;
340 static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
342 struct leases_db_fetch_state *state =
343 (struct leases_db_fetch_state *)private_data;
344 DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
345 enum ndr_err_code ndr_err;
346 struct leases_db_value *value;
348 value = talloc(talloc_tos(), struct leases_db_value);
349 if (value == NULL) {
350 state->status = NT_STATUS_NO_MEMORY;
351 return;
354 ndr_err = ndr_pull_struct_blob_all(
355 &blob, value, value,
356 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
357 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
358 DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
359 __func__, ndr_errstr(ndr_err)));
360 TALLOC_FREE(value);
361 state->status = ndr_map_error2ntstatus(ndr_err);
362 return;
365 if (DEBUGLEVEL >= 10) {
366 DEBUG(10, ("%s:\n", __func__));
367 NDR_PRINT_DEBUG(leases_db_value, value);
370 state->parser(value->num_files,
371 value->files,
372 state->private_data);
374 TALLOC_FREE(value);
375 state->status = NT_STATUS_OK;
378 NTSTATUS leases_db_parse(const struct GUID *client_guid,
379 const struct smb2_lease_key *lease_key,
380 void (*parser)(uint32_t num_files,
381 const struct leases_db_file *files,
382 void *private_data),
383 void *private_data)
385 struct leases_db_key_buf keybuf;
386 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
387 struct leases_db_fetch_state state;
388 NTSTATUS status;
390 if (!leases_db_init(true)) {
391 return NT_STATUS_INTERNAL_ERROR;
394 state = (struct leases_db_fetch_state) {
395 .parser = parser,
396 .private_data = private_data,
397 .status = NT_STATUS_OK
400 status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
401 &state);
402 if (!NT_STATUS_IS_OK(status)) {
403 return status;
405 return state.status;
408 struct leases_db_rename_state {
409 const struct file_id *id;
410 const char *servicename_new;
411 const char *filename_new;
412 const char *stream_name_new;
413 NTSTATUS status;
416 static void leases_db_rename_fn(
417 struct leases_db_value *value, bool *modified, void *private_data)
419 struct leases_db_rename_state *state = private_data;
420 struct leases_db_file *file = NULL;
421 uint32_t i;
423 /* id must exist. */
424 for (i = 0; i < value->num_files; i++) {
425 if (file_id_equal(state->id, &value->files[i].id)) {
426 break;
429 if (i == value->num_files) {
430 state->status = NT_STATUS_NOT_FOUND;
431 return;
434 file = &value->files[i];
435 file->servicepath = state->servicename_new;
436 file->base_name = state->filename_new;
437 file->stream_name = state->stream_name_new;
439 *modified = true;
442 NTSTATUS leases_db_rename(const struct GUID *client_guid,
443 const struct smb2_lease_key *lease_key,
444 const struct file_id *id,
445 const char *servicename_new,
446 const char *filename_new,
447 const char *stream_name_new)
449 struct leases_db_rename_state state = {
450 .id = id,
451 .servicename_new = servicename_new,
452 .filename_new = filename_new,
453 .stream_name_new = stream_name_new,
455 NTSTATUS status;
457 status = leases_db_do_locked(
458 client_guid, lease_key, leases_db_rename_fn, &state);
459 if (!NT_STATUS_IS_OK(status)) {
460 DBG_DEBUG("leases_db_do_locked failed: %s\n",
461 nt_errstr(status));
462 return status;
464 return state.status;
467 struct leases_db_set_state {
468 uint32_t current_state;
469 bool breaking;
470 uint32_t breaking_to_requested;
471 uint32_t breaking_to_required;
472 uint16_t lease_version;
473 uint16_t epoch;
476 static void leases_db_set_fn(
477 struct leases_db_value *value, bool *modified, void *private_data)
479 struct leases_db_set_state *state = private_data;
481 if (value->num_files == 0) {
482 DBG_WARNING("leases_db_set on new entry\n");
483 return;
485 value->current_state = state->current_state;
486 value->breaking = state->breaking;
487 value->breaking_to_requested = state->breaking_to_requested;
488 value->breaking_to_required = state->breaking_to_required;
489 value->lease_version = state->lease_version;
490 value->epoch = state->epoch;
491 *modified = true;
494 NTSTATUS leases_db_set(const struct GUID *client_guid,
495 const struct smb2_lease_key *lease_key,
496 uint32_t current_state,
497 bool breaking,
498 uint32_t breaking_to_requested,
499 uint32_t breaking_to_required,
500 uint16_t lease_version,
501 uint16_t epoch)
503 struct leases_db_set_state state = {
504 .current_state = current_state,
505 .breaking = breaking,
506 .breaking_to_requested = breaking_to_requested,
507 .breaking_to_required = breaking_to_required,
508 .lease_version = lease_version,
509 .epoch = epoch,
511 NTSTATUS status;
513 status = leases_db_do_locked(
514 client_guid, lease_key, leases_db_set_fn, &state);
515 if (!NT_STATUS_IS_OK(status)) {
516 DBG_DEBUG("leases_db_do_locked failed: %s\n",
517 nt_errstr(status));
518 return status;
520 return NT_STATUS_OK;
523 struct leases_db_get_state {
524 const struct file_id *file_id;
525 uint32_t *current_state;
526 bool *breaking;
527 uint32_t *breaking_to_requested;
528 uint32_t *breaking_to_required;
529 uint16_t *lease_version;
530 uint16_t *epoch;
531 NTSTATUS status;
534 static void leases_db_get_fn(TDB_DATA key, TDB_DATA data, void *private_data)
536 struct leases_db_get_state *state = private_data;
537 DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
538 enum ndr_err_code ndr_err;
539 struct leases_db_value *value;
540 uint32_t i;
542 value = talloc(talloc_tos(), struct leases_db_value);
543 if (value == NULL) {
544 state->status = NT_STATUS_NO_MEMORY;
545 return;
548 ndr_err = ndr_pull_struct_blob_all(
549 &blob, value, value,
550 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
551 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
552 DBG_DEBUG("ndr_pull_struct_blob_failed: %s\n",
553 ndr_errstr(ndr_err));
554 TALLOC_FREE(value);
555 state->status = ndr_map_error2ntstatus(ndr_err);
556 return;
559 if (DEBUGLEVEL >= 10) {
560 DBG_DEBUG("\n");
561 NDR_PRINT_DEBUG(leases_db_value, value);
564 /* id must exist. */
565 for (i = 0; i < value->num_files; i++) {
566 if (file_id_equal(state->file_id, &value->files[i].id)) {
567 break;
571 if (i == value->num_files) {
572 state->status = NT_STATUS_NOT_FOUND;
573 TALLOC_FREE(value);
574 return;
577 if (state->current_state != NULL) {
578 *state->current_state = value->current_state;
580 if (state->breaking != NULL) {
581 *state->breaking = value->breaking;
583 if (state->breaking_to_requested != NULL) {
584 *state->breaking_to_requested = value->breaking_to_requested;
586 if (state->breaking_to_required != NULL) {
587 *state->breaking_to_required = value->breaking_to_required;
589 if (state->lease_version != NULL) {
590 *state->lease_version = value->lease_version;
592 if (state->epoch != NULL) {
593 *state->epoch = value->epoch;
596 TALLOC_FREE(value);
597 state->status = NT_STATUS_OK;
600 NTSTATUS leases_db_get(const struct GUID *client_guid,
601 const struct smb2_lease_key *lease_key,
602 const struct file_id *file_id,
603 uint32_t *current_state,
604 bool *breaking,
605 uint32_t *breaking_to_requested,
606 uint32_t *breaking_to_required,
607 uint16_t *lease_version,
608 uint16_t *epoch)
610 struct leases_db_get_state state = {
611 .file_id = file_id,
612 .current_state = current_state,
613 .breaking = breaking,
614 .breaking_to_requested = breaking_to_requested,
615 .breaking_to_required = breaking_to_required,
616 .lease_version = lease_version,
617 .epoch = epoch,
619 struct leases_db_key_buf keybuf;
620 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
621 NTSTATUS status;
623 if (!leases_db_init(true)) {
624 return NT_STATUS_INTERNAL_ERROR;
627 status = dbwrap_parse_record(
628 leases_db, db_key, leases_db_get_fn, &state);
629 if (!NT_STATUS_IS_OK(status)) {
630 return status;
632 return state.status;
635 struct leases_db_get_current_state_state {
636 int seqnum;
637 uint32_t current_state;
638 NTSTATUS status;
642 * This function is an optimization that
643 * relies on the fact that the
644 * smb2_lease_state current_state
645 * (which is a uint32_t size)
646 * from struct leases_db_value is the first
647 * entry in the ndr-encoded struct leases_db_value.
648 * Read it without having to ndr decode all
649 * the values in struct leases_db_value.
652 static void leases_db_get_current_state_fn(
653 TDB_DATA key, TDB_DATA data, void *private_data)
655 struct leases_db_get_current_state_state *state = private_data;
656 struct ndr_pull ndr;
657 enum ndr_err_code ndr_err;
659 if (data.dsize < sizeof(uint32_t)) {
660 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
661 return;
664 state->seqnum = dbwrap_get_seqnum(leases_db);
666 ndr = (struct ndr_pull) {
667 .data = data.dptr, .data_size = data.dsize,
669 ndr_err = ndr_pull_uint32(&ndr, NDR_SCALARS, &state->current_state);
670 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
671 state->status = ndr_map_error2ntstatus(ndr_err);
675 NTSTATUS leases_db_get_current_state(
676 const struct GUID *client_guid,
677 const struct smb2_lease_key *lease_key,
678 int *database_seqnum,
679 uint32_t *current_state)
681 struct leases_db_get_current_state_state state = { 0 };
682 struct leases_db_key_buf keybuf;
683 TDB_DATA db_key = { 0 };
684 NTSTATUS status;
686 if (!leases_db_init(true)) {
687 return NT_STATUS_INTERNAL_ERROR;
690 state.seqnum = dbwrap_get_seqnum(leases_db);
691 if (*database_seqnum == state.seqnum) {
692 return NT_STATUS_OK;
695 db_key = leases_db_key(&keybuf, client_guid, lease_key);
697 status = dbwrap_parse_record(
698 leases_db, db_key, leases_db_get_current_state_fn, &state);
699 if (!NT_STATUS_IS_OK(status)) {
700 return status;
702 *database_seqnum = state.seqnum;
703 *current_state = state.current_state;
705 return NT_STATUS_OK;
708 NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx,
709 uint32_t num_files,
710 const struct leases_db_file *files,
711 struct file_id **pp_ids)
713 uint32_t i;
714 struct file_id *ids = talloc_array(mem_ctx,
715 struct file_id,
716 num_files);
717 if (ids == NULL) {
718 return NT_STATUS_NO_MEMORY;
721 for (i = 0; i < num_files; i++) {
722 ids[i] = files[i].id;
724 *pp_ids = ids;
725 return NT_STATUS_OK;