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/>.
22 #include "system/filesys.h"
23 #include "locking/leases_db.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
28 #include "librpc/gen_ndr/ndr_leases_db.h"
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)
44 db_path = lock_path(talloc_tos(), "leases.tdb");
45 if (db_path == NULL) {
49 leases_db = db_open(NULL, db_path, 0,
54 TDB_INCOMPATIBLE_HASH,
55 read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
56 DBWRAP_LOCK_ORDER_4, DBWRAP_FLAG_NONE);
58 if (leases_db == NULL) {
59 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
66 struct leases_db_key_buf {
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) {
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,
100 static void leases_db_do_locked_fn(
101 struct db_record *rec,
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);
113 state->status = NT_STATUS_NO_MEMORY;
117 if (blob.length != 0) {
118 ndr_err = ndr_pull_struct_blob_all(
122 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
123 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
124 DBG_ERR("ndr_pull_struct_blob_failed: %s\n",
125 ndr_errstr(ndr_err));
126 state->status = ndr_map_error2ntstatus(ndr_err);
131 state->fn(value, &modified, state->private_data);
137 if (value->num_files == 0) {
138 state->status = dbwrap_record_delete(rec);
139 if (!NT_STATUS_IS_OK(state->status)) {
140 DBG_ERR("dbwrap_record_delete returned %s\n",
141 nt_errstr(state->status));
146 ndr_err = ndr_push_struct_blob(
150 (ndr_push_flags_fn_t)ndr_push_leases_db_value);
151 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
152 DBG_ERR("ndr_push_struct_blob_failed: %s\n",
153 ndr_errstr(ndr_err));
154 state->status = ndr_map_error2ntstatus(ndr_err);
158 if (DEBUGLEVEL >= 10) {
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_ERR("dbwrap_record_store returned %s\n",
168 nt_errstr(state->status));
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,
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,
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)) {
202 struct leases_db_add_state {
203 const struct file_id *id;
204 uint32_t current_state;
205 uint16_t lease_version;
207 const char *servicepath;
208 const char *base_name;
209 const char *stream_name;
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;
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;
228 if (value->num_files == 0) {
230 value->current_state = state->current_state;
231 value->lease_version = state->lease_version;
232 value->epoch = state->epoch;
235 tmp = talloc_realloc(
238 struct leases_db_file,
239 value->num_files + 1);
241 state->status = NT_STATUS_NO_MEMORY;
246 value->files[value->num_files] = (struct leases_db_file) {
248 .servicepath = state->servicepath,
249 .base_name = state->base_name,
250 .stream_name = state->stream_name,
252 value->num_files += 1;
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,
263 const char *servicepath,
264 const char *base_name,
265 const char *stream_name)
267 struct leases_db_add_state state = {
269 .current_state = current_state,
270 .lease_version = lease_version,
272 .servicepath = servicepath,
273 .base_name = base_name,
274 .stream_name = stream_name,
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",
288 struct leases_db_del_state {
289 const struct file_id *id;
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;
299 for (i = 0; i < value->num_files; i++) {
300 if (file_id_equal(state->id, &value->files[i].id)) {
304 if (i == value->num_files) {
305 state->status = NT_STATUS_NOT_FOUND;
309 value->files[i] = value->files[value->num_files-1];
310 value->num_files -= 1;
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 };
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",
332 struct leases_db_fetch_state {
333 void (*parser)(uint32_t num_files,
334 const struct leases_db_file *files,
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);
350 state->status = NT_STATUS_NO_MEMORY;
354 ndr_err = ndr_pull_struct_blob_all(
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)));
361 state->status = ndr_map_error2ntstatus(ndr_err);
365 if (DEBUGLEVEL >= 10) {
366 DEBUG(10, ("%s:\n", __func__));
367 NDR_PRINT_DEBUG(leases_db_value, value);
370 state->parser(value->num_files,
372 state->private_data);
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,
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;
390 if (!leases_db_init(true)) {
391 return NT_STATUS_INTERNAL_ERROR;
394 state = (struct leases_db_fetch_state) {
396 .private_data = private_data,
397 .status = NT_STATUS_OK
400 status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
402 if (!NT_STATUS_IS_OK(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;
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;
424 for (i = 0; i < value->num_files; i++) {
425 if (file_id_equal(state->id, &value->files[i].id)) {
429 if (i == value->num_files) {
430 state->status = NT_STATUS_NOT_FOUND;
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;
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 = {
451 .servicename_new = servicename_new,
452 .filename_new = filename_new,
453 .stream_name_new = stream_name_new,
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",
467 struct leases_db_set_state {
468 uint32_t current_state;
470 uint32_t breaking_to_requested;
471 uint32_t breaking_to_required;
472 uint16_t lease_version;
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");
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;
494 NTSTATUS leases_db_set(const struct GUID *client_guid,
495 const struct smb2_lease_key *lease_key,
496 uint32_t current_state,
498 uint32_t breaking_to_requested,
499 uint32_t breaking_to_required,
500 uint16_t lease_version,
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,
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",
523 struct leases_db_get_state {
524 const struct file_id *file_id;
525 uint32_t *current_state;
527 uint32_t *breaking_to_requested;
528 uint32_t *breaking_to_required;
529 uint16_t *lease_version;
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;
542 value = talloc(talloc_tos(), struct leases_db_value);
544 state->status = NT_STATUS_NO_MEMORY;
548 ndr_err = ndr_pull_struct_blob_all(
550 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
551 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
552 DBG_ERR("ndr_pull_struct_blob_failed: %s\n",
553 ndr_errstr(ndr_err));
555 state->status = ndr_map_error2ntstatus(ndr_err);
559 if (DEBUGLEVEL >= 10) {
561 NDR_PRINT_DEBUG(leases_db_value, value);
565 for (i = 0; i < value->num_files; i++) {
566 if (file_id_equal(state->file_id, &value->files[i].id)) {
571 if (i == value->num_files) {
572 state->status = NT_STATUS_NOT_FOUND;
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;
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,
605 uint32_t *breaking_to_requested,
606 uint32_t *breaking_to_required,
607 uint16_t *lease_version,
610 struct leases_db_get_state state = {
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,
619 struct leases_db_key_buf keybuf;
620 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
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)) {
635 struct leases_db_get_current_state_state {
637 uint32_t current_state;
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;
657 enum ndr_err_code ndr_err;
659 if (data.dsize < sizeof(uint32_t)) {
660 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
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 };
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) {
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)) {
702 *database_seqnum = state.seqnum;
703 *current_state = state.current_state;
708 NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx,
710 const struct leases_db_file *files,
711 struct file_id **pp_ids)
714 struct file_id *ids = talloc_array(mem_ctx,
718 return NT_STATUS_NO_MEMORY;
721 for (i = 0; i < num_files; i++) {
722 ids[i] = files[i].id;