s3: smbd: Allow fchmod from the NFS-style mode ACL in set_nt_acl() for a SMB2 POSIX...
[Samba.git] / source3 / locking / leases_db.c
blobeae58f5fc824df579c23091ce65afc06636779ce
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,
50 SMBD_VOLATILE_TDB_HASH_SIZE,
51 SMBD_VOLATILE_TDB_FLAGS |
52 TDB_SEQNUM,
53 read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
54 DBWRAP_LOCK_ORDER_4, DBWRAP_FLAG_NONE);
55 TALLOC_FREE(db_path);
56 if (leases_db == NULL) {
57 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
58 return false;
61 return true;
64 struct leases_db_key_buf {
65 uint8_t buf[32];
68 static TDB_DATA leases_db_key(struct leases_db_key_buf *buf,
69 const struct GUID *client_guid,
70 const struct smb2_lease_key *lease_key)
72 struct leases_db_key db_key = {
73 .client_guid = *client_guid,
74 .lease_key = *lease_key };
75 DATA_BLOB blob = { .data = buf->buf, .length = sizeof(buf->buf) };
76 enum ndr_err_code ndr_err;
78 if (DEBUGLEVEL >= 10) {
79 DBG_DEBUG("\n");
80 NDR_PRINT_DEBUG(leases_db_key, &db_key);
83 ndr_err = ndr_push_struct_into_fixed_blob(
84 &blob, &db_key, (ndr_push_flags_fn_t)ndr_push_leases_db_key);
85 SMB_ASSERT(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
87 return (TDB_DATA) { .dptr = buf->buf, .dsize = sizeof(buf->buf) };
90 struct leases_db_do_locked_state {
91 void (*fn)(struct leases_db_value *value,
92 bool *modified,
93 void *private_data);
94 void *private_data;
95 NTSTATUS status;
98 static void leases_db_do_locked_fn(
99 struct db_record *rec,
100 TDB_DATA db_value,
101 void *private_data)
103 struct leases_db_do_locked_state *state = private_data;
104 DATA_BLOB blob = { .data = db_value.dptr, .length = db_value.dsize };
105 struct leases_db_value *value = NULL;
106 enum ndr_err_code ndr_err;
107 bool modified = false;
109 value = talloc_zero(talloc_tos(), struct leases_db_value);
110 if (value == NULL) {
111 state->status = NT_STATUS_NO_MEMORY;
112 goto done;
115 if (blob.length != 0) {
116 ndr_err = ndr_pull_struct_blob_all(
117 &blob,
118 value,
119 value,
120 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
121 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
122 DBG_ERR("ndr_pull_struct_blob_failed: %s\n",
123 ndr_errstr(ndr_err));
124 state->status = ndr_map_error2ntstatus(ndr_err);
125 goto done;
129 state->fn(value, &modified, state->private_data);
131 if (!modified) {
132 goto done;
135 if (value->num_files == 0) {
136 state->status = dbwrap_record_delete(rec);
137 if (!NT_STATUS_IS_OK(state->status)) {
138 DBG_ERR("dbwrap_record_delete returned %s\n",
139 nt_errstr(state->status));
141 goto done;
144 ndr_err = ndr_push_struct_blob(
145 &blob,
146 value,
147 value,
148 (ndr_push_flags_fn_t)ndr_push_leases_db_value);
149 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
150 DBG_ERR("ndr_push_struct_blob_failed: %s\n",
151 ndr_errstr(ndr_err));
152 state->status = ndr_map_error2ntstatus(ndr_err);
153 goto done;
156 if (DEBUGLEVEL >= 10) {
157 DBG_DEBUG("\n");
158 NDR_PRINT_DEBUG(leases_db_value, value);
161 db_value = make_tdb_data(blob.data, blob.length);
163 state->status = dbwrap_record_store(rec, db_value, 0);
164 if (!NT_STATUS_IS_OK(state->status)) {
165 DBG_ERR("dbwrap_record_store returned %s\n",
166 nt_errstr(state->status));
169 done:
170 TALLOC_FREE(value);
173 static NTSTATUS leases_db_do_locked(
174 const struct GUID *client_guid,
175 const struct smb2_lease_key *lease_key,
176 void (*fn)(struct leases_db_value *value,
177 bool *modified,
178 void *private_data),
179 void *private_data)
181 struct leases_db_key_buf keybuf;
182 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
183 struct leases_db_do_locked_state state = {
184 .fn = fn, .private_data = private_data,
186 NTSTATUS status;
188 if (!leases_db_init(false)) {
189 return NT_STATUS_INTERNAL_ERROR;
192 status = dbwrap_do_locked(
193 leases_db, db_key, leases_db_do_locked_fn, &state);
194 if (!NT_STATUS_IS_OK(status)) {
195 return status;
197 return state.status;
200 struct leases_db_add_state {
201 const struct file_id *id;
202 uint32_t current_state;
203 uint16_t lease_version;
204 uint16_t epoch;
205 const char *servicepath;
206 const char *base_name;
207 const char *stream_name;
208 NTSTATUS status;
211 static void leases_db_add_fn(
212 struct leases_db_value *value, bool *modified, void *private_data)
214 struct leases_db_add_state *state = private_data;
215 struct leases_db_file *tmp = NULL;
216 uint32_t i;
218 /* id must be unique. */
219 for (i = 0; i < value->num_files; i++) {
220 if (file_id_equal(state->id, &value->files[i].id)) {
221 state->status = NT_STATUS_OBJECT_NAME_COLLISION;
222 return;
226 if (value->num_files == 0) {
227 /* new record */
228 value->current_state = state->current_state;
229 value->lease_version = state->lease_version;
230 value->epoch = state->epoch;
233 tmp = talloc_realloc(
234 value,
235 value->files,
236 struct leases_db_file,
237 value->num_files + 1);
238 if (tmp == NULL) {
239 state->status = NT_STATUS_NO_MEMORY;
240 return;
242 value->files = tmp;
244 value->files[value->num_files] = (struct leases_db_file) {
245 .id = *state->id,
246 .servicepath = state->servicepath,
247 .base_name = state->base_name,
248 .stream_name = state->stream_name,
250 value->num_files += 1;
252 *modified = true;
255 NTSTATUS leases_db_add(const struct GUID *client_guid,
256 const struct smb2_lease_key *lease_key,
257 const struct file_id *id,
258 uint32_t current_state,
259 uint16_t lease_version,
260 uint16_t epoch,
261 const char *servicepath,
262 const char *base_name,
263 const char *stream_name)
265 struct leases_db_add_state state = {
266 .id = id,
267 .current_state = current_state,
268 .lease_version = lease_version,
269 .epoch = epoch,
270 .servicepath = servicepath,
271 .base_name = base_name,
272 .stream_name = stream_name,
274 NTSTATUS status;
276 status = leases_db_do_locked(
277 client_guid, lease_key, leases_db_add_fn, &state);
278 if (!NT_STATUS_IS_OK(status)) {
279 DBG_DEBUG("leases_db_do_locked failed: %s\n",
280 nt_errstr(status));
281 return status;
283 return state.status;
286 struct leases_db_del_state {
287 const struct file_id *id;
288 NTSTATUS status;
291 static void leases_db_del_fn(
292 struct leases_db_value *value, bool *modified, void *private_data)
294 struct leases_db_del_state *state = private_data;
295 uint32_t i;
297 for (i = 0; i < value->num_files; i++) {
298 if (file_id_equal(state->id, &value->files[i].id)) {
299 break;
302 if (i == value->num_files) {
303 state->status = NT_STATUS_NOT_FOUND;
304 return;
307 value->files[i] = value->files[value->num_files-1];
308 value->num_files -= 1;
310 *modified = true;
313 NTSTATUS leases_db_del(const struct GUID *client_guid,
314 const struct smb2_lease_key *lease_key,
315 const struct file_id *id)
317 struct leases_db_del_state state = { .id = id };
318 NTSTATUS status;
320 status = leases_db_do_locked(
321 client_guid, lease_key, leases_db_del_fn, &state);
322 if (!NT_STATUS_IS_OK(status)) {
323 DBG_DEBUG("leases_db_do_locked failed: %s\n",
324 nt_errstr(status));
325 return status;
327 return state.status;
330 struct leases_db_fetch_state {
331 void (*parser)(uint32_t num_files,
332 const struct leases_db_file *files,
333 void *private_data);
334 void *private_data;
335 NTSTATUS status;
338 static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
340 struct leases_db_fetch_state *state =
341 (struct leases_db_fetch_state *)private_data;
342 DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
343 enum ndr_err_code ndr_err;
344 struct leases_db_value *value;
346 value = talloc(talloc_tos(), struct leases_db_value);
347 if (value == NULL) {
348 state->status = NT_STATUS_NO_MEMORY;
349 return;
352 ndr_err = ndr_pull_struct_blob_all(
353 &blob, value, value,
354 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
355 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
356 DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
357 __func__, ndr_errstr(ndr_err)));
358 TALLOC_FREE(value);
359 state->status = ndr_map_error2ntstatus(ndr_err);
360 return;
363 if (DEBUGLEVEL >= 10) {
364 DEBUG(10, ("%s:\n", __func__));
365 NDR_PRINT_DEBUG(leases_db_value, value);
368 state->parser(value->num_files,
369 value->files,
370 state->private_data);
372 TALLOC_FREE(value);
373 state->status = NT_STATUS_OK;
376 NTSTATUS leases_db_parse(const struct GUID *client_guid,
377 const struct smb2_lease_key *lease_key,
378 void (*parser)(uint32_t num_files,
379 const struct leases_db_file *files,
380 void *private_data),
381 void *private_data)
383 struct leases_db_key_buf keybuf;
384 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
385 struct leases_db_fetch_state state;
386 NTSTATUS status;
388 if (!leases_db_init(true)) {
389 return NT_STATUS_INTERNAL_ERROR;
392 state = (struct leases_db_fetch_state) {
393 .parser = parser,
394 .private_data = private_data,
395 .status = NT_STATUS_OK
398 status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
399 &state);
400 if (!NT_STATUS_IS_OK(status)) {
401 return status;
403 return state.status;
406 struct leases_db_rename_state {
407 const struct file_id *id;
408 const char *servicename_new;
409 const char *filename_new;
410 const char *stream_name_new;
411 NTSTATUS status;
414 static void leases_db_rename_fn(
415 struct leases_db_value *value, bool *modified, void *private_data)
417 struct leases_db_rename_state *state = private_data;
418 struct leases_db_file *file = NULL;
419 uint32_t i;
421 /* id must exist. */
422 for (i = 0; i < value->num_files; i++) {
423 if (file_id_equal(state->id, &value->files[i].id)) {
424 break;
427 if (i == value->num_files) {
428 state->status = NT_STATUS_NOT_FOUND;
429 return;
432 file = &value->files[i];
433 file->servicepath = state->servicename_new;
434 file->base_name = state->filename_new;
435 file->stream_name = state->stream_name_new;
437 *modified = true;
440 NTSTATUS leases_db_rename(const struct GUID *client_guid,
441 const struct smb2_lease_key *lease_key,
442 const struct file_id *id,
443 const char *servicename_new,
444 const char *filename_new,
445 const char *stream_name_new)
447 struct leases_db_rename_state state = {
448 .id = id,
449 .servicename_new = servicename_new,
450 .filename_new = filename_new,
451 .stream_name_new = stream_name_new,
453 NTSTATUS status;
455 status = leases_db_do_locked(
456 client_guid, lease_key, leases_db_rename_fn, &state);
457 if (!NT_STATUS_IS_OK(status)) {
458 DBG_DEBUG("leases_db_do_locked failed: %s\n",
459 nt_errstr(status));
460 return status;
462 return state.status;
465 struct leases_db_set_state {
466 uint32_t current_state;
467 bool breaking;
468 uint32_t breaking_to_requested;
469 uint32_t breaking_to_required;
470 uint16_t lease_version;
471 uint16_t epoch;
474 static void leases_db_set_fn(
475 struct leases_db_value *value, bool *modified, void *private_data)
477 struct leases_db_set_state *state = private_data;
479 if (value->num_files == 0) {
480 DBG_WARNING("leases_db_set on new entry\n");
481 return;
483 value->current_state = state->current_state;
484 value->breaking = state->breaking;
485 value->breaking_to_requested = state->breaking_to_requested;
486 value->breaking_to_required = state->breaking_to_required;
487 value->lease_version = state->lease_version;
488 value->epoch = state->epoch;
489 *modified = true;
492 NTSTATUS leases_db_set(const struct GUID *client_guid,
493 const struct smb2_lease_key *lease_key,
494 uint32_t current_state,
495 bool breaking,
496 uint32_t breaking_to_requested,
497 uint32_t breaking_to_required,
498 uint16_t lease_version,
499 uint16_t epoch)
501 struct leases_db_set_state state = {
502 .current_state = current_state,
503 .breaking = breaking,
504 .breaking_to_requested = breaking_to_requested,
505 .breaking_to_required = breaking_to_required,
506 .lease_version = lease_version,
507 .epoch = epoch,
509 NTSTATUS status;
511 status = leases_db_do_locked(
512 client_guid, lease_key, leases_db_set_fn, &state);
513 if (!NT_STATUS_IS_OK(status)) {
514 DBG_DEBUG("leases_db_do_locked failed: %s\n",
515 nt_errstr(status));
516 return status;
518 return NT_STATUS_OK;
521 struct leases_db_get_state {
522 const struct file_id *file_id;
523 uint32_t *current_state;
524 bool *breaking;
525 uint32_t *breaking_to_requested;
526 uint32_t *breaking_to_required;
527 uint16_t *lease_version;
528 uint16_t *epoch;
529 NTSTATUS status;
532 static void leases_db_get_fn(TDB_DATA key, TDB_DATA data, void *private_data)
534 struct leases_db_get_state *state = private_data;
535 DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
536 enum ndr_err_code ndr_err;
537 struct leases_db_value *value;
538 uint32_t i;
540 value = talloc(talloc_tos(), struct leases_db_value);
541 if (value == NULL) {
542 state->status = NT_STATUS_NO_MEMORY;
543 return;
546 ndr_err = ndr_pull_struct_blob_all(
547 &blob, value, value,
548 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
549 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
550 DBG_ERR("ndr_pull_struct_blob_failed: %s\n",
551 ndr_errstr(ndr_err));
552 TALLOC_FREE(value);
553 state->status = ndr_map_error2ntstatus(ndr_err);
554 return;
557 if (DEBUGLEVEL >= 10) {
558 DBG_DEBUG("\n");
559 NDR_PRINT_DEBUG(leases_db_value, value);
562 /* id must exist. */
563 for (i = 0; i < value->num_files; i++) {
564 if (file_id_equal(state->file_id, &value->files[i].id)) {
565 break;
569 if (i == value->num_files) {
570 state->status = NT_STATUS_NOT_FOUND;
571 TALLOC_FREE(value);
572 return;
575 if (state->current_state != NULL) {
576 *state->current_state = value->current_state;
578 if (state->breaking != NULL) {
579 *state->breaking = value->breaking;
581 if (state->breaking_to_requested != NULL) {
582 *state->breaking_to_requested = value->breaking_to_requested;
584 if (state->breaking_to_required != NULL) {
585 *state->breaking_to_required = value->breaking_to_required;
587 if (state->lease_version != NULL) {
588 *state->lease_version = value->lease_version;
590 if (state->epoch != NULL) {
591 *state->epoch = value->epoch;
594 TALLOC_FREE(value);
595 state->status = NT_STATUS_OK;
598 NTSTATUS leases_db_get(const struct GUID *client_guid,
599 const struct smb2_lease_key *lease_key,
600 const struct file_id *file_id,
601 uint32_t *current_state,
602 bool *breaking,
603 uint32_t *breaking_to_requested,
604 uint32_t *breaking_to_required,
605 uint16_t *lease_version,
606 uint16_t *epoch)
608 struct leases_db_get_state state = {
609 .file_id = file_id,
610 .current_state = current_state,
611 .breaking = breaking,
612 .breaking_to_requested = breaking_to_requested,
613 .breaking_to_required = breaking_to_required,
614 .lease_version = lease_version,
615 .epoch = epoch,
617 struct leases_db_key_buf keybuf;
618 TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
619 NTSTATUS status;
621 if (!leases_db_init(true)) {
622 return NT_STATUS_INTERNAL_ERROR;
625 status = dbwrap_parse_record(
626 leases_db, db_key, leases_db_get_fn, &state);
627 if (!NT_STATUS_IS_OK(status)) {
628 return status;
630 return state.status;
633 struct leases_db_get_current_state_state {
634 int seqnum;
635 uint32_t current_state;
636 NTSTATUS status;
640 * This function is an optimization that
641 * relies on the fact that the
642 * smb2_lease_state current_state
643 * (which is a uint32_t size)
644 * from struct leases_db_value is the first
645 * entry in the ndr-encoded struct leases_db_value.
646 * Read it without having to ndr decode all
647 * the values in struct leases_db_value.
650 static void leases_db_get_current_state_fn(
651 TDB_DATA key, TDB_DATA data, void *private_data)
653 struct leases_db_get_current_state_state *state = private_data;
654 struct ndr_pull ndr;
655 enum ndr_err_code ndr_err;
657 if (data.dsize < sizeof(uint32_t)) {
658 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
659 return;
662 state->seqnum = dbwrap_get_seqnum(leases_db);
664 ndr = (struct ndr_pull) {
665 .data = data.dptr, .data_size = data.dsize,
667 ndr_err = ndr_pull_uint32(&ndr, NDR_SCALARS, &state->current_state);
668 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
669 state->status = ndr_map_error2ntstatus(ndr_err);
673 NTSTATUS leases_db_get_current_state(
674 const struct GUID *client_guid,
675 const struct smb2_lease_key *lease_key,
676 int *database_seqnum,
677 uint32_t *current_state)
679 struct leases_db_get_current_state_state state = { 0 };
680 struct leases_db_key_buf keybuf;
681 TDB_DATA db_key = { 0 };
682 NTSTATUS status;
684 if (!leases_db_init(true)) {
685 return NT_STATUS_INTERNAL_ERROR;
688 state.seqnum = dbwrap_get_seqnum(leases_db);
689 if (*database_seqnum == state.seqnum) {
690 return NT_STATUS_OK;
693 db_key = leases_db_key(&keybuf, client_guid, lease_key);
695 status = dbwrap_parse_record(
696 leases_db, db_key, leases_db_get_current_state_fn, &state);
697 if (!NT_STATUS_IS_OK(status)) {
698 return status;
700 *database_seqnum = state.seqnum;
701 *current_state = state.current_state;
703 return NT_STATUS_OK;
706 NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx,
707 uint32_t num_files,
708 const struct leases_db_file *files,
709 struct file_id **pp_ids)
711 uint32_t i;
712 struct file_id *ids = talloc_array(mem_ctx,
713 struct file_id,
714 num_files);
715 if (ids == NULL) {
716 return NT_STATUS_NO_MEMORY;
719 for (i = 0; i < num_files; i++) {
720 ids[i] = files[i].id;
722 *pp_ids = ids;
723 return NT_STATUS_OK;