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
,
50 SMBD_VOLATILE_TDB_HASH_SIZE
,
51 SMBD_VOLATILE_TDB_FLAGS
|
53 read_only
? O_RDONLY
: O_RDWR
|O_CREAT
, 0644,
54 DBWRAP_LOCK_ORDER_4
, DBWRAP_FLAG_NONE
);
56 if (leases_db
== NULL
) {
57 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
64 struct leases_db_key_buf
{
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) {
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
,
98 static void leases_db_do_locked_fn(
99 struct db_record
*rec
,
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
);
111 state
->status
= NT_STATUS_NO_MEMORY
;
115 if (blob
.length
!= 0) {
116 ndr_err
= ndr_pull_struct_blob_all(
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
);
129 state
->fn(value
, &modified
, state
->private_data
);
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
));
144 ndr_err
= ndr_push_struct_blob(
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
);
156 if (DEBUGLEVEL
>= 10) {
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
));
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
,
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
,
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
)) {
200 struct leases_db_add_state
{
201 const struct file_id
*id
;
202 uint32_t current_state
;
203 uint16_t lease_version
;
205 const char *servicepath
;
206 const char *base_name
;
207 const char *stream_name
;
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
;
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
;
226 if (value
->num_files
== 0) {
228 value
->current_state
= state
->current_state
;
229 value
->lease_version
= state
->lease_version
;
230 value
->epoch
= state
->epoch
;
233 tmp
= talloc_realloc(
236 struct leases_db_file
,
237 value
->num_files
+ 1);
239 state
->status
= NT_STATUS_NO_MEMORY
;
244 value
->files
[value
->num_files
] = (struct leases_db_file
) {
246 .servicepath
= state
->servicepath
,
247 .base_name
= state
->base_name
,
248 .stream_name
= state
->stream_name
,
250 value
->num_files
+= 1;
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
,
261 const char *servicepath
,
262 const char *base_name
,
263 const char *stream_name
)
265 struct leases_db_add_state state
= {
267 .current_state
= current_state
,
268 .lease_version
= lease_version
,
270 .servicepath
= servicepath
,
271 .base_name
= base_name
,
272 .stream_name
= stream_name
,
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",
286 struct leases_db_del_state
{
287 const struct file_id
*id
;
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
;
297 for (i
= 0; i
< value
->num_files
; i
++) {
298 if (file_id_equal(state
->id
, &value
->files
[i
].id
)) {
302 if (i
== value
->num_files
) {
303 state
->status
= NT_STATUS_NOT_FOUND
;
307 value
->files
[i
] = value
->files
[value
->num_files
-1];
308 value
->num_files
-= 1;
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
};
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",
330 struct leases_db_fetch_state
{
331 void (*parser
)(uint32_t num_files
,
332 const struct leases_db_file
*files
,
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
);
348 state
->status
= NT_STATUS_NO_MEMORY
;
352 ndr_err
= ndr_pull_struct_blob_all(
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
)));
359 state
->status
= ndr_map_error2ntstatus(ndr_err
);
363 if (DEBUGLEVEL
>= 10) {
364 DEBUG(10, ("%s:\n", __func__
));
365 NDR_PRINT_DEBUG(leases_db_value
, value
);
368 state
->parser(value
->num_files
,
370 state
->private_data
);
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
,
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
;
388 if (!leases_db_init(true)) {
389 return NT_STATUS_INTERNAL_ERROR
;
392 state
= (struct leases_db_fetch_state
) {
394 .private_data
= private_data
,
395 .status
= NT_STATUS_OK
398 status
= dbwrap_parse_record(leases_db
, db_key
, leases_db_parser
,
400 if (!NT_STATUS_IS_OK(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
;
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
;
422 for (i
= 0; i
< value
->num_files
; i
++) {
423 if (file_id_equal(state
->id
, &value
->files
[i
].id
)) {
427 if (i
== value
->num_files
) {
428 state
->status
= NT_STATUS_NOT_FOUND
;
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
;
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
= {
449 .servicename_new
= servicename_new
,
450 .filename_new
= filename_new
,
451 .stream_name_new
= stream_name_new
,
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",
465 struct leases_db_set_state
{
466 uint32_t current_state
;
468 uint32_t breaking_to_requested
;
469 uint32_t breaking_to_required
;
470 uint16_t lease_version
;
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");
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
;
492 NTSTATUS
leases_db_set(const struct GUID
*client_guid
,
493 const struct smb2_lease_key
*lease_key
,
494 uint32_t current_state
,
496 uint32_t breaking_to_requested
,
497 uint32_t breaking_to_required
,
498 uint16_t lease_version
,
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
,
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",
521 struct leases_db_get_state
{
522 const struct file_id
*file_id
;
523 uint32_t *current_state
;
525 uint32_t *breaking_to_requested
;
526 uint32_t *breaking_to_required
;
527 uint16_t *lease_version
;
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
;
540 value
= talloc(talloc_tos(), struct leases_db_value
);
542 state
->status
= NT_STATUS_NO_MEMORY
;
546 ndr_err
= ndr_pull_struct_blob_all(
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
));
553 state
->status
= ndr_map_error2ntstatus(ndr_err
);
557 if (DEBUGLEVEL
>= 10) {
559 NDR_PRINT_DEBUG(leases_db_value
, value
);
563 for (i
= 0; i
< value
->num_files
; i
++) {
564 if (file_id_equal(state
->file_id
, &value
->files
[i
].id
)) {
569 if (i
== value
->num_files
) {
570 state
->status
= NT_STATUS_NOT_FOUND
;
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
;
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
,
603 uint32_t *breaking_to_requested
,
604 uint32_t *breaking_to_required
,
605 uint16_t *lease_version
,
608 struct leases_db_get_state state
= {
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
,
617 struct leases_db_key_buf keybuf
;
618 TDB_DATA db_key
= leases_db_key(&keybuf
, client_guid
, lease_key
);
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
)) {
633 struct leases_db_get_current_state_state
{
635 uint32_t current_state
;
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
;
655 enum ndr_err_code ndr_err
;
657 if (data
.dsize
< sizeof(uint32_t)) {
658 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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 };
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
) {
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
)) {
700 *database_seqnum
= state
.seqnum
;
701 *current_state
= state
.current_state
;
706 NTSTATUS
leases_db_copy_file_ids(TALLOC_CTX
*mem_ctx
,
708 const struct leases_db_file
*files
,
709 struct file_id
**pp_ids
)
712 struct file_id
*ids
= talloc_array(mem_ctx
,
716 return NT_STATUS_NO_MEMORY
;
719 for (i
= 0; i
< num_files
; i
++) {
720 ids
[i
] = files
[i
].id
;