2 Unix SMB/CIFS implementation.
4 trivial database library
6 Copyright (C) Andrew Tridgell 1999-2005
7 Copyright (C) Paul `Rusty' Russell 2000
8 Copyright (C) Jeremy Allison 2000-2003
10 ** NOTE! The following LGPL license applies to the tdb
11 ** library. This does NOT imply that all of Samba is released
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public
16 License as published by the Free Software Foundation; either
17 version 3 of the License, or (at your option) any later version.
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
24 You should have received a copy of the GNU Lesser General Public
25 License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #include "tdb1_private.h"
31 /* We use two hashes to double-check they're using the right hash function. */
32 void tdb1_header_hash(struct tdb_context
*tdb
,
33 uint32_t *magic1_hash
, uint32_t *magic2_hash
)
35 uint32_t tdb1_magic
= TDB1_MAGIC
;
37 *magic1_hash
= tdb_hash(tdb
, TDB_MAGIC_FOOD
, sizeof(TDB_MAGIC_FOOD
));
38 *magic2_hash
= tdb_hash(tdb
, TDB1_CONV(tdb1_magic
), sizeof(tdb1_magic
));
40 /* Make sure at least one hash is non-zero! */
41 if (*magic1_hash
== 0 && *magic2_hash
== 0)
45 static void tdb_context_init(struct tdb_context
*tdb
,
46 struct tdb_attribute_tdb1_max_dead
*max_dead
)
48 assert(tdb
->flags
& TDB_VERSION1
);
52 tdb
->tdb1
.traverse_read
= tdb
->tdb1
.traverse_write
= 0;
53 memset(&tdb
->tdb1
.travlocks
, 0, sizeof(tdb
->tdb1
.travlocks
));
54 tdb
->tdb1
.transaction
= NULL
;
56 /* cache the page size */
57 tdb
->tdb1
.page_size
= getpagesize();
58 if (tdb
->tdb1
.page_size
<= 0) {
59 tdb
->tdb1
.page_size
= 0x2000;
63 tdb
->tdb1
.max_dead_records
= max_dead
->max_dead
;
65 tdb
->tdb1
.max_dead_records
= 0;
69 /* initialise a new database */
70 enum TDB_ERROR
tdb1_new_database(struct tdb_context
*tdb
,
71 struct tdb_attribute_tdb1_hashsize
*hashsize
,
72 struct tdb_attribute_tdb1_max_dead
*max_dead
)
74 struct tdb1_header
*newdb
;
76 int hash_size
= TDB1_DEFAULT_HASH_SIZE
;
79 tdb_context_init(tdb
, max_dead
);
81 /* Default TDB2 hash becomes default TDB1 hash. */
82 if (tdb
->hash_fn
== tdb_jenkins_hash
)
83 tdb
->hash_fn
= tdb1_old_hash
;
86 hash_size
= hashsize
->hsize
;
88 /* We make it up in memory, then write it out if not internal */
89 size
= sizeof(struct tdb1_header
) + (hash_size
+1)*sizeof(tdb1_off_t
);
90 if (!(newdb
= (struct tdb1_header
*)calloc(size
, 1))) {
91 return tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
92 "Could not allocate new database header");
95 /* Fill in the header */
96 newdb
->version
= TDB1_VERSION
;
97 newdb
->hash_size
= hash_size
;
99 tdb1_header_hash(tdb
, &newdb
->magic1_hash
, &newdb
->magic2_hash
);
101 /* Make sure older tdbs (which don't check the magic hash fields)
102 * will refuse to open this TDB. */
103 if (tdb
->hash_fn
== tdb1_incompatible_hash
)
104 newdb
->rwlocks
= TDB1_HASH_RWLOCK_MAGIC
;
106 memcpy(&tdb
->tdb1
.header
, newdb
, sizeof(tdb
->tdb1
.header
));
107 /* This creates an endian-converted db. */
109 /* Don't endian-convert the magic food! */
110 memcpy(newdb
->magic_food
, TDB_MAGIC_FOOD
, strlen(TDB_MAGIC_FOOD
)+1);
112 if (tdb
->flags
& TDB_INTERNAL
) {
113 tdb
->file
->map_size
= size
;
114 tdb
->file
->map_ptr
= (char *)newdb
;
117 if (lseek(tdb
->file
->fd
, 0, SEEK_SET
) == -1) {
118 ret
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
119 "tdb1_new_database: lseek failed");
123 if (ftruncate(tdb
->file
->fd
, 0) == -1) {
124 ret
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
125 "tdb1_new_database: ftruncate failed");
129 if (!tdb1_write_all(tdb
->file
->fd
, newdb
, size
)) {
130 ret
= tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
131 "tdb1_new_database: write failed");
141 typedef void (*tdb1_log_func
)(struct tdb_context
*, enum tdb_log_level
, enum TDB_ERROR
,
142 const char *, void *);
143 typedef uint64_t (*tdb1_hash_func
)(const void *key
, size_t len
, uint64_t seed
,
146 struct tdb1_logging_context
{
147 tdb1_log_func log_fn
;
151 static bool hash_correct(struct tdb_context
*tdb
,
152 uint32_t *m1
, uint32_t *m2
)
154 /* older TDB without magic hash references */
155 if (tdb
->tdb1
.header
.magic1_hash
== 0
156 && tdb
->tdb1
.header
.magic2_hash
== 0) {
160 tdb1_header_hash(tdb
, m1
, m2
);
161 return (tdb
->tdb1
.header
.magic1_hash
== *m1
&&
162 tdb
->tdb1
.header
.magic2_hash
== *m2
);
165 static bool check_header_hash(struct tdb_context
*tdb
,
166 uint32_t *m1
, uint32_t *m2
)
168 if (hash_correct(tdb
, m1
, m2
))
171 /* If they use one inbuilt, try the other inbuilt hash. */
172 if (tdb
->hash_fn
== tdb1_old_hash
)
173 tdb
->hash_fn
= tdb1_incompatible_hash
;
174 else if (tdb
->hash_fn
== tdb1_incompatible_hash
)
175 tdb
->hash_fn
= tdb1_old_hash
;
178 return hash_correct(tdb
, m1
, m2
);
181 /* We are hold the TDB open lock on tdb->fd. */
182 enum TDB_ERROR
tdb1_open(struct tdb_context
*tdb
,
183 struct tdb_attribute_tdb1_max_dead
*max_dead
)
185 const char *hash_alg
;
186 uint32_t magic1
, magic2
;
188 tdb
->flags
|= TDB_VERSION1
;
190 tdb_context_init(tdb
, max_dead
);
192 /* Default TDB2 hash becomes default TDB1 hash. */
193 if (tdb
->hash_fn
== tdb_jenkins_hash
) {
194 tdb
->hash_fn
= tdb1_old_hash
;
195 hash_alg
= "default";
196 } else if (tdb
->hash_fn
== tdb1_incompatible_hash
)
197 hash_alg
= "tdb1_incompatible_hash";
199 hash_alg
= "the user defined";
201 if (tdb
->tdb1
.header
.version
!= TDB1_BYTEREV(TDB1_VERSION
)) {
202 if (tdb
->flags
& TDB_CONVERT
) {
203 return tdb_logerr(tdb
, TDB_ERR_IO
, TDB_LOG_ERROR
,
205 " %s does not need TDB_CONVERT",
209 tdb
->flags
|= TDB_CONVERT
;
210 tdb1_convert(&tdb
->tdb1
.header
, sizeof(tdb
->tdb1
.header
));
213 if (tdb
->tdb1
.header
.rwlocks
!= 0 &&
214 tdb
->tdb1
.header
.rwlocks
!= TDB1_HASH_RWLOCK_MAGIC
) {
215 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_ERROR
,
216 "tdb1_open: spinlocks no longer supported");
219 if (!check_header_hash(tdb
, &magic1
, &magic2
)) {
220 return tdb_logerr(tdb
, TDB_ERR_CORRUPT
, TDB_LOG_USE_ERROR
,
222 "%s was not created with %s hash function we are using\n"
223 "magic1_hash[0x%08X %s 0x%08X] "
224 "magic2_hash[0x%08X %s 0x%08X]",
226 tdb
->tdb1
.header
.magic1_hash
,
227 (tdb
->tdb1
.header
.magic1_hash
== magic1
) ? "==" : "!=",
229 tdb
->tdb1
.header
.magic2_hash
,
230 (tdb
->tdb1
.header
.magic2_hash
== magic2
) ? "==" : "!=",