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
;