2 Unix SMB/CIFS implementation.
4 Copyright (C) 2004-2007, Jelmer Vernooij, jelmer@samba.org
5 Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "lib/ldb/include/ldb.h"
24 #include "lib/ldb/include/ldb_errors.h"
26 #include "librpc/gen_ndr/winreg.h"
27 #include "param/param.h"
29 static struct hive_operations reg_backend_ldb
;
34 struct ldb_context
*ldb
;
36 struct ldb_message
**subkeys
, **values
;
37 int subkey_count
, value_count
;
40 static void reg_ldb_unpack_value(TALLOC_CTX
*mem_ctx
,
41 struct smb_iconv_convenience
*iconv_convenience
,
42 struct ldb_message
*msg
,
43 const char **name
, uint32_t *type
,
46 const struct ldb_val
*val
;
50 *name
= talloc_strdup(mem_ctx
,
51 ldb_msg_find_attr_as_string(msg
, "value",
54 value_type
= ldb_msg_find_attr_as_uint(msg
, "type", 0);
57 val
= ldb_msg_find_ldb_val(msg
, "data");
63 data
->length
= convert_string_talloc(mem_ctx
, iconv_convenience
, CH_UNIX
, CH_UTF16
,
64 val
->data
, val
->length
,
65 (void **)&data
->data
);
70 *data
= strhex_to_data_blob((char *)val
->data
);
78 uint32_t tmp
= strtoul((char *)val
->data
, NULL
, 0);
79 *data
= data_blob_talloc(mem_ctx
, &tmp
, 4);
84 *data
= data_blob_talloc(mem_ctx
, val
->data
, val
->length
);
89 static struct ldb_message
*reg_ldb_pack_value(struct ldb_context
*ctx
,
92 uint32_t type
, DATA_BLOB data
)
95 struct ldb_message
*msg
= talloc_zero(mem_ctx
, struct ldb_message
);
98 ldb_msg_add_string(msg
, "value", talloc_strdup(mem_ctx
, name
));
103 val
.length
= convert_string_talloc(mem_ctx
, lp_iconv_convenience(global_loadparm
), CH_UTF16
, CH_UNIX
,
107 ldb_msg_add_value(msg
, "data", &val
, NULL
);
111 ldb_msg_add_string(msg
, "data",
112 data_blob_hex_string(mem_ctx
, &data
));
116 ldb_msg_add_string(msg
, "data",
117 talloc_asprintf(mem_ctx
, "0x%x",
118 IVAL(data
.data
, 0)));
121 ldb_msg_add_value(msg
, "data", &data
, NULL
);
125 type_s
= talloc_asprintf(mem_ctx
, "%u", type
);
126 ldb_msg_add_string(msg
, "type", type_s
);
131 static char *reg_ldb_escape(TALLOC_CTX
*mem_ctx
, const char *value
)
135 val
.data
= discard_const_p(uint8_t, value
);
136 val
.length
= strlen(value
);
138 return ldb_dn_escape_value(mem_ctx
, val
);
141 static int reg_close_ldb_key(struct ldb_key_data
*key
)
143 if (key
->subkeys
!= NULL
) {
144 talloc_free(key
->subkeys
);
148 if (key
->values
!= NULL
) {
149 talloc_free(key
->values
);
155 static struct ldb_dn
*reg_path_to_ldb(TALLOC_CTX
*mem_ctx
,
156 const struct hive_key
*from
,
157 const char *path
, const char *add
)
159 TALLOC_CTX
*local_ctx
;
161 char *mypath
= talloc_strdup(mem_ctx
, path
);
163 struct ldb_key_data
*kd
= talloc_get_type(from
, struct ldb_key_data
);
164 struct ldb_context
*ldb
= kd
->ldb
;
166 local_ctx
= talloc_new(mem_ctx
);
169 ret
= ldb_dn_new(mem_ctx
, ldb
, add
);
171 ret
= ldb_dn_new(mem_ctx
, ldb
, NULL
);
173 if (!ldb_dn_validate(ret
)) {
175 talloc_free(local_ctx
);
182 begin
= strrchr(mypath
, '\\');
184 if (begin
) keyname
= begin
+ 1;
185 else keyname
= mypath
;
187 if(strlen(keyname
)) {
188 if (!ldb_dn_add_base_fmt(ret
, "key=%s",
189 reg_ldb_escape(local_ctx
,
192 talloc_free(local_ctx
);
204 ldb_dn_add_base(ret
, kd
->dn
);
206 talloc_free(local_ctx
);
211 static WERROR
cache_subkeys(struct ldb_key_data
*kd
)
213 struct ldb_context
*c
= kd
->ldb
;
214 struct ldb_result
*res
;
217 ret
= ldb_search(c
, kd
->dn
, LDB_SCOPE_ONELEVEL
, "(key=*)", NULL
, &res
);
219 if (ret
!= LDB_SUCCESS
) {
220 DEBUG(0, ("Error getting subkeys for '%s': %s\n",
221 ldb_dn_get_linearized(kd
->dn
), ldb_errstring(c
)));
225 kd
->subkey_count
= res
->count
;
226 kd
->subkeys
= talloc_steal(kd
, res
->msgs
);
232 static WERROR
cache_values(struct ldb_key_data
*kd
)
234 struct ldb_context
*c
= kd
->ldb
;
235 struct ldb_result
*res
;
238 ret
= ldb_search(c
, kd
->dn
, LDB_SCOPE_ONELEVEL
,
239 "(value=*)", NULL
, &res
);
241 if (ret
!= LDB_SUCCESS
) {
242 DEBUG(0, ("Error getting values for '%s': %s\n",
243 ldb_dn_get_linearized(kd
->dn
), ldb_errstring(c
)));
247 kd
->value_count
= res
->count
;
248 kd
->values
= talloc_steal(kd
, res
->msgs
);
255 static WERROR
ldb_get_subkey_by_id(TALLOC_CTX
*mem_ctx
,
256 const struct hive_key
*k
, uint32_t idx
,
258 const char **classname
,
259 NTTIME
*last_mod_time
)
261 struct ldb_message_element
*el
;
262 struct ldb_key_data
*kd
= talloc_get_type(k
, struct ldb_key_data
);
267 if (classname
!= NULL
)
268 *classname
= NULL
; /* TODO: Store properly */
269 if (last_mod_time
!= NULL
)
270 *last_mod_time
= 0; /* TODO: we need to add this to the
271 ldb backend properly */
273 /* Do a search if necessary */
274 if (kd
->subkeys
== NULL
) {
275 W_ERROR_NOT_OK_RETURN(cache_subkeys(kd
));
278 if (idx
>= kd
->subkey_count
)
279 return WERR_NO_MORE_ITEMS
;
281 el
= ldb_msg_find_element(kd
->subkeys
[idx
], "key");
282 SMB_ASSERT(el
!= NULL
);
283 SMB_ASSERT(el
->num_values
!= 0);
286 *name
= talloc_strdup(mem_ctx
, (char *)el
->values
[0].data
);
291 static WERROR
ldb_get_default_value(TALLOC_CTX
*mem_ctx
, struct hive_key
*k
,
292 const char** name
, uint32_t *data_type
,
295 struct ldb_key_data
*kd
= talloc_get_type(k
, struct ldb_key_data
);
296 struct ldb_context
*c
= kd
->ldb
;
297 const char* attrs
[] = { "data", "type", NULL
};
298 struct ldb_result
*res
;
301 ret
= ldb_search(c
, kd
->dn
, LDB_SCOPE_BASE
, "", attrs
, &res
);
303 if (ret
!= LDB_SUCCESS
) {
304 DEBUG(0, ("Error getting default value for '%s': %s\n",
305 ldb_dn_get_linearized(kd
->dn
), ldb_errstring(c
)));
309 if (res
->count
== 0 || res
->msgs
[0]->num_elements
== 0)
312 reg_ldb_unpack_value(mem_ctx
, lp_iconv_convenience(global_loadparm
),
313 res
->msgs
[0], name
, data_type
, data
);
320 static WERROR
ldb_get_value_by_id(TALLOC_CTX
*mem_ctx
, struct hive_key
*k
,
321 int idx
, const char **name
,
322 uint32_t *data_type
, DATA_BLOB
*data
)
324 struct ldb_key_data
*kd
= talloc_get_type(k
, struct ldb_key_data
);
326 /* if default value exists, give it back */
327 if (W_ERROR_IS_OK(ldb_get_default_value(mem_ctx
, k
, name
, data_type
,
335 /* Do the search if necessary */
336 if (kd
->values
== NULL
) {
337 W_ERROR_NOT_OK_RETURN(cache_values(kd
));
340 if (idx
>= kd
->value_count
)
341 return WERR_NO_MORE_ITEMS
;
343 reg_ldb_unpack_value(mem_ctx
, lp_iconv_convenience(global_loadparm
),
344 kd
->values
[idx
], name
, data_type
, data
);
349 static WERROR
ldb_get_value(TALLOC_CTX
*mem_ctx
, struct hive_key
*k
,
350 const char *name
, uint32_t *data_type
,
353 struct ldb_key_data
*kd
= talloc_get_type(k
, struct ldb_key_data
);
354 struct ldb_context
*c
= kd
->ldb
;
355 struct ldb_result
*res
;
359 if (strlen(name
) == 0) {
361 return ldb_get_default_value(mem_ctx
, k
, NULL
, data_type
, data
);
364 query
= talloc_asprintf(mem_ctx
, "(value=%s)", name
);
365 ret
= ldb_search(c
, kd
->dn
, LDB_SCOPE_ONELEVEL
, query
, NULL
, &res
);
368 if (ret
!= LDB_SUCCESS
) {
369 DEBUG(0, ("Error getting values for '%s': %s\n",
370 ldb_dn_get_linearized(kd
->dn
), ldb_errstring(c
)));
377 reg_ldb_unpack_value(mem_ctx
, lp_iconv_convenience(global_loadparm
),
378 res
->msgs
[0], NULL
, data_type
, data
);
386 static WERROR
ldb_open_key(TALLOC_CTX
*mem_ctx
, const struct hive_key
*h
,
387 const char *name
, struct hive_key
**key
)
389 struct ldb_result
*res
;
390 struct ldb_dn
*ldap_path
;
392 struct ldb_key_data
*newkd
;
393 struct ldb_key_data
*kd
= talloc_get_type(h
, struct ldb_key_data
);
394 struct ldb_context
*c
= kd
->ldb
;
396 ldap_path
= reg_path_to_ldb(mem_ctx
, h
, name
, NULL
);
398 ret
= ldb_search(c
, ldap_path
, LDB_SCOPE_BASE
, "(key=*)", NULL
, &res
);
400 if (ret
!= LDB_SUCCESS
) {
401 DEBUG(3, ("Error opening key '%s': %s\n",
402 ldb_dn_get_linearized(ldap_path
), ldb_errstring(c
)));
404 } else if (res
->count
== 0) {
405 DEBUG(3, ("Key '%s' not found\n",
406 ldb_dn_get_linearized(ldap_path
)));
411 newkd
= talloc_zero(mem_ctx
, struct ldb_key_data
);
412 newkd
->key
.ops
= ®_backend_ldb
;
413 newkd
->ldb
= talloc_reference(newkd
, kd
->ldb
);
414 newkd
->dn
= ldb_dn_copy(mem_ctx
, res
->msgs
[0]->dn
);
416 *key
= (struct hive_key
*)newkd
;
421 WERROR
reg_open_ldb_file(TALLOC_CTX
*parent_ctx
, const char *location
,
422 struct auth_session_info
*session_info
,
423 struct cli_credentials
*credentials
,
424 struct event_context
*ev_ctx
,
425 struct loadparm_context
*lp_ctx
,
428 struct ldb_key_data
*kd
;
429 struct ldb_context
*wrap
;
430 struct ldb_message
*attrs_msg
;
432 if (location
== NULL
)
433 return WERR_INVALID_PARAM
;
435 wrap
= ldb_wrap_connect(parent_ctx
, ev_ctx
, lp_ctx
,
436 location
, session_info
, credentials
, 0, NULL
);
439 DEBUG(1, (__FILE__
": unable to connect\n"));
443 attrs_msg
= ldb_msg_new(wrap
);
444 W_ERROR_HAVE_NO_MEMORY(attrs_msg
);
445 attrs_msg
->dn
= ldb_dn_new(attrs_msg
, wrap
, "@ATTRIBUTES");
446 W_ERROR_HAVE_NO_MEMORY(attrs_msg
->dn
);
447 ldb_msg_add_string(attrs_msg
, "key", "CASE_INSENSITIVE");
448 ldb_msg_add_string(attrs_msg
, "value", "CASE_INSENSITIVE");
450 ldb_add(wrap
, attrs_msg
);
452 ldb_set_debug_stderr(wrap
);
454 kd
= talloc_zero(parent_ctx
, struct ldb_key_data
);
455 kd
->key
.ops
= ®_backend_ldb
;
456 kd
->ldb
= talloc_reference(kd
, wrap
);
457 talloc_set_destructor (kd
, reg_close_ldb_key
);
458 kd
->dn
= ldb_dn_new(kd
, wrap
, "hive=NONE");
460 *k
= (struct hive_key
*)kd
;
465 static WERROR
ldb_add_key(TALLOC_CTX
*mem_ctx
, const struct hive_key
*parent
,
466 const char *name
, const char *classname
,
467 struct security_descriptor
*sd
,
468 struct hive_key
**newkey
)
470 struct ldb_key_data
*parentkd
= discard_const_p(struct ldb_key_data
, parent
);
471 struct ldb_message
*msg
;
472 struct ldb_key_data
*newkd
;
475 msg
= ldb_msg_new(mem_ctx
);
477 msg
->dn
= reg_path_to_ldb(msg
, parent
, name
, NULL
);
479 ldb_msg_add_string(msg
, "key", talloc_strdup(mem_ctx
, name
));
480 if (classname
!= NULL
)
481 ldb_msg_add_string(msg
, "classname",
482 talloc_strdup(mem_ctx
, classname
));
484 ret
= ldb_add(parentkd
->ldb
, msg
);
485 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
486 return WERR_ALREADY_EXISTS
;
489 if (ret
!= LDB_SUCCESS
) {
490 DEBUG(1, ("ldb_add: %s\n", ldb_errstring(parentkd
->ldb
)));
494 DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(msg
->dn
)));
496 newkd
= talloc_zero(mem_ctx
, struct ldb_key_data
);
497 newkd
->ldb
= talloc_reference(newkd
, parentkd
->ldb
);
498 newkd
->key
.ops
= ®_backend_ldb
;
499 newkd
->dn
= talloc_steal(newkd
, msg
->dn
);
501 *newkey
= (struct hive_key
*)newkd
;
504 talloc_free(parentkd
->subkeys
);
505 parentkd
->subkeys
= NULL
;
510 static WERROR
ldb_del_value (struct hive_key
*key
, const char *child
)
513 struct ldb_key_data
*kd
= talloc_get_type(key
, struct ldb_key_data
);
515 struct ldb_message
*msg
;
516 struct ldb_dn
*childdn
;
518 if (strlen(child
) == 0) {
520 mem_ctx
= talloc_init("ldb_del_value");
522 msg
= talloc_zero(mem_ctx
, struct ldb_message
);
523 msg
->dn
= ldb_dn_copy(msg
, kd
->dn
);
524 ldb_msg_add_empty(msg
, "data", LDB_FLAG_MOD_DELETE
, NULL
);
525 ldb_msg_add_empty(msg
, "type", LDB_FLAG_MOD_DELETE
, NULL
);
527 ret
= ldb_modify(kd
->ldb
, msg
);
528 if (ret
!= LDB_SUCCESS
) {
529 DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd
->ldb
)));
530 talloc_free(mem_ctx
);
534 talloc_free(mem_ctx
);
537 childdn
= ldb_dn_copy(kd
->ldb
, kd
->dn
);
538 if (!ldb_dn_add_child_fmt(childdn
, "value=%s",
539 reg_ldb_escape(childdn
, child
)))
541 talloc_free(childdn
);
545 ret
= ldb_delete(kd
->ldb
, childdn
);
547 talloc_free(childdn
);
549 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
551 } else if (ret
!= LDB_SUCCESS
) {
552 DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd
->ldb
)));
558 talloc_free(kd
->values
);
564 static WERROR
ldb_del_key(const struct hive_key
*key
, const char *name
)
567 struct ldb_key_data
*parentkd
= talloc_get_type(key
, struct ldb_key_data
);
568 struct ldb_dn
*ldap_path
;
569 TALLOC_CTX
*mem_ctx
= talloc_init("ldb_del_key");
570 struct ldb_context
*c
= parentkd
->ldb
;
571 struct ldb_result
*res_keys
;
572 struct ldb_result
*res_vals
;
576 /* Verify key exists by opening it */
577 werr
= ldb_open_key(mem_ctx
, key
, name
, &hk
);
578 if (!W_ERROR_IS_OK(werr
)) {
579 talloc_free(mem_ctx
);
583 ldap_path
= reg_path_to_ldb(mem_ctx
, key
, name
, NULL
);
585 talloc_free(mem_ctx
);
589 /* Search for subkeys */
590 ret
= ldb_search(c
, ldap_path
, LDB_SCOPE_ONELEVEL
,
591 "(key=*)", NULL
, &res_keys
);
593 if (ret
!= LDB_SUCCESS
) {
594 DEBUG(0, ("Error getting subkeys for '%s': %s\n",
595 ldb_dn_get_linearized(ldap_path
), ldb_errstring(c
)));
596 talloc_free(mem_ctx
);
600 /* Search for values */
601 ret
= ldb_search(c
, ldap_path
, LDB_SCOPE_ONELEVEL
,
602 "(value=*)", NULL
, &res_vals
);
604 if (ret
!= LDB_SUCCESS
) {
605 DEBUG(0, ("Error getting values for '%s': %s\n",
606 ldb_dn_get_linearized(ldap_path
), ldb_errstring(c
)));
607 talloc_free(mem_ctx
);
611 /* Start an explicit transaction */
612 ret
= ldb_transaction_start(c
);
614 if (ret
!= LDB_SUCCESS
) {
615 DEBUG(0, ("ldb_transaction_start: %s\n", ldb_errstring(c
)));
616 talloc_free(mem_ctx
);
620 if (res_keys
->count
|| res_vals
->count
)
622 /* Delete any subkeys */
623 for (i
= 0; i
< res_keys
->count
; i
++)
625 werr
= ldb_del_key(hk
, ldb_msg_find_attr_as_string(
628 if (!W_ERROR_IS_OK(werr
)) {
629 ret
= ldb_transaction_cancel(c
);
630 talloc_free(mem_ctx
);
635 /* Delete any values */
636 for (i
= 0; i
< res_vals
->count
; i
++)
638 werr
= ldb_del_value(hk
, ldb_msg_find_attr_as_string(
641 if (!W_ERROR_IS_OK(werr
)) {
642 ret
= ldb_transaction_cancel(c
);
643 talloc_free(mem_ctx
);
649 /* Delete the key itself */
650 ret
= ldb_delete(c
, ldap_path
);
652 if (ret
!= LDB_SUCCESS
)
654 DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(c
)));
655 ret
= ldb_transaction_cancel(c
);
656 talloc_free(mem_ctx
);
660 /* Commit the transaction */
661 ret
= ldb_transaction_commit(c
);
663 if (ret
!= LDB_SUCCESS
)
665 DEBUG(0, ("ldb_transaction_commit: %s\n", ldb_errstring(c
)));
666 ret
= ldb_transaction_cancel(c
);
667 talloc_free(mem_ctx
);
671 talloc_free(mem_ctx
);
674 talloc_free(parentkd
->subkeys
);
675 parentkd
->subkeys
= NULL
;
680 static WERROR
ldb_set_value(struct hive_key
*parent
,
681 const char *name
, uint32_t type
,
682 const DATA_BLOB data
)
684 struct ldb_message
*msg
;
685 struct ldb_key_data
*kd
= talloc_get_type(parent
, struct ldb_key_data
);
687 TALLOC_CTX
*mem_ctx
= talloc_init("ldb_set_value");
689 msg
= reg_ldb_pack_value(kd
->ldb
, mem_ctx
, name
, type
, data
);
690 msg
->dn
= ldb_dn_copy(msg
, kd
->dn
);
692 if (strlen(name
) > 0) {
693 /* For a default value, we add/overwrite the attributes to/of the hive.
694 For a normal value, we create new childs. */
695 if (!ldb_dn_add_child_fmt(msg
->dn
, "value=%s",
696 reg_ldb_escape(mem_ctx
, name
)))
698 talloc_free(mem_ctx
);
703 ret
= ldb_add(kd
->ldb
, msg
);
704 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
706 for (i
= 0; i
< msg
->num_elements
; i
++) {
707 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
709 ret
= ldb_modify(kd
->ldb
, msg
);
712 if (ret
!= LDB_SUCCESS
) {
713 DEBUG(1, ("ldb_set_value: %s\n", ldb_errstring(kd
->ldb
)));
714 talloc_free(mem_ctx
);
719 talloc_free(kd
->values
);
722 talloc_free(mem_ctx
);
726 static WERROR
ldb_get_key_info(TALLOC_CTX
*mem_ctx
,
727 const struct hive_key
*key
,
728 const char **classname
,
729 uint32_t *num_subkeys
,
730 uint32_t *num_values
,
731 NTTIME
*last_change_time
,
732 uint32_t *max_subkeynamelen
,
733 uint32_t *max_valnamelen
,
734 uint32_t *max_valbufsize
)
736 struct ldb_key_data
*kd
= talloc_get_type(key
, struct ldb_key_data
);
739 if (classname
!= NULL
)
741 if (num_subkeys
!= NULL
)
743 if (num_values
!= NULL
)
745 if (last_change_time
!= NULL
)
746 *last_change_time
= 0;
747 if (max_subkeynamelen
!= NULL
)
748 *max_subkeynamelen
= 0;
749 if (max_valnamelen
!= NULL
)
751 if (max_valbufsize
!= NULL
)
754 if (kd
->subkeys
== NULL
) {
755 W_ERROR_NOT_OK_RETURN(cache_subkeys(kd
));
758 if (kd
->values
== NULL
) {
759 W_ERROR_NOT_OK_RETURN(cache_values(kd
));
762 if (num_subkeys
!= NULL
) {
763 *num_subkeys
= kd
->subkey_count
;
765 if (num_values
!= NULL
) {
766 *num_values
= kd
->value_count
;
770 if (max_subkeynamelen
!= NULL
) {
772 struct ldb_message_element
*el
;
774 *max_subkeynamelen
= 0;
776 for (i
= 0; i
< kd
->subkey_count
; i
++) {
777 el
= ldb_msg_find_element(kd
->subkeys
[i
], "key");
778 *max_subkeynamelen
= MAX(*max_subkeynamelen
, el
->values
[0].length
);
782 if (max_valnamelen
!= NULL
|| max_valbufsize
!= NULL
) {
784 struct ldb_message_element
*el
;
785 W_ERROR_NOT_OK_RETURN(cache_values(kd
));
787 if (max_valbufsize
!= NULL
)
790 if (max_valnamelen
!= NULL
)
793 for (i
= 0; i
< kd
->value_count
; i
++) {
794 if (max_valnamelen
!= NULL
) {
795 el
= ldb_msg_find_element(kd
->values
[i
], "value");
796 *max_valnamelen
= MAX(*max_valnamelen
, el
->values
[0].length
);
799 if (max_valbufsize
!= NULL
) {
801 reg_ldb_unpack_value(mem_ctx
,
802 lp_iconv_convenience(global_loadparm
),
805 *max_valbufsize
= MAX(*max_valbufsize
, data
.length
);
806 talloc_free(data
.data
);
814 static struct hive_operations reg_backend_ldb
= {
816 .add_key
= ldb_add_key
,
817 .del_key
= ldb_del_key
,
818 .get_key_by_name
= ldb_open_key
,
819 .enum_value
= ldb_get_value_by_id
,
820 .enum_key
= ldb_get_subkey_by_id
,
821 .set_value
= ldb_set_value
,
822 .get_value_by_name
= ldb_get_value
,
823 .delete_value
= ldb_del_value
,
824 .get_key_info
= ldb_get_key_info
,