Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : trivial database library
5 :
6 : Copyright (C) Andrew Tridgell 1999-2005
7 : Copyright (C) Paul `Rusty' Russell 2000
8 : Copyright (C) Jeremy Allison 2000-2003
9 :
10 : ** NOTE! The following LGPL license applies to the tdb
11 : ** library. This does NOT imply that all of Samba is released
12 : ** under the LGPL
13 :
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.
18 :
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.
23 :
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/>.
26 : */
27 :
28 : #include "tdb_private.h"
29 :
30 : #define TDB_NEXT_LOCK_ERR ((tdb_off_t)-1)
31 :
32 : /* Uses traverse lock: 0 = finish, TDB_NEXT_LOCK_ERR = error,
33 : other = record offset */
34 390395697 : static tdb_off_t tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock,
35 : struct tdb_record *rec)
36 : {
37 390395697 : int want_next = (tlock->off != 0);
38 :
39 : /* Lock each chain from the start one. */
40 593229659 : for (; tlock->list < tdb->hash_size; tlock->list++) {
41 588997380 : if (!tlock->off && tlock->list != 0) {
42 : /* this is an optimisation for the common case where
43 : the hash chain is empty, which is particularly
44 : common for the use of tdb with ldb, where large
45 : hashes are used. In that case we spend most of our
46 : time in tdb_brlock(), locking empty hash chains.
47 :
48 : To avoid this, we do an unlocked pre-check to see
49 : if the hash chain is empty before starting to look
50 : inside it. If it is empty then we can avoid that
51 : hash chain. If it isn't empty then we can't believe
52 : the value we get back, as we read it without a
53 : lock, so instead we get the lock and re-fetch the
54 : value below.
55 :
56 : Notice that not doing this optimisation on the
57 : first hash chain is critical. We must guarantee
58 : that we have done at least one fcntl lock at the
59 : start of a search to guarantee that memory is
60 : coherent on SMP systems. If records are added by
61 : others during the search then thats OK, and we
62 : could possibly miss those with this trick, but we
63 : could miss them anyway without this trick, so the
64 : semantics don't change.
65 :
66 : With a non-indexed ldb search this trick gains us a
67 : factor of around 80 in speed on a linux 2.6.x
68 : system (testing using ldbtest).
69 : */
70 198601683 : tdb->methods->next_hash_chain(tdb, &tlock->list);
71 198601683 : if (tlock->list == tdb->hash_size) {
72 3828063 : continue;
73 : }
74 : }
75 :
76 585169317 : if (tdb_lock(tdb, tlock->list, tlock->lock_rw) == -1)
77 0 : return TDB_NEXT_LOCK_ERR;
78 :
79 : /* No previous record? Start at top of chain. */
80 585169317 : if (!tlock->off) {
81 199005940 : if (tdb_ofs_read(tdb, TDB_HASH_TOP(tlock->list),
82 199005940 : &tlock->off) == -1)
83 0 : goto fail;
84 : } else {
85 : /* Otherwise unlock the previous record. */
86 386163377 : if (tdb_unlock_record(tdb, tlock->off) != 0)
87 0 : goto fail;
88 : }
89 :
90 585169317 : if (want_next) {
91 : /* We have offset of old record: grab next */
92 386163377 : if (tdb_rec_read(tdb, tlock->off, rec) == -1)
93 0 : goto fail;
94 386163377 : tlock->off = rec->next;
95 : }
96 :
97 : /* Iterate through chain */
98 936826003 : while( tlock->off) {
99 386182183 : if (tdb_rec_read(tdb, tlock->off, rec) == -1)
100 0 : goto fail;
101 :
102 : /* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */
103 386182183 : if (tlock->off == rec->next) {
104 0 : tdb->ecode = TDB_ERR_CORRUPT;
105 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n"));
106 0 : goto fail;
107 : }
108 :
109 386182183 : if (!TDB_DEAD(rec)) {
110 : /* Woohoo: we found one! */
111 386163418 : if (tdb_lock_record(tdb, tlock->off) != 0)
112 0 : goto fail;
113 386163418 : return tlock->off;
114 : }
115 :
116 18765 : tlock->off = rec->next;
117 : }
118 199005899 : tdb_unlock(tdb, tlock->list, tlock->lock_rw);
119 199005899 : want_next = 0;
120 : }
121 : /* We finished iteration without finding anything */
122 4232279 : tdb->ecode = TDB_SUCCESS;
123 4232279 : return 0;
124 :
125 0 : fail:
126 0 : tlock->off = 0;
127 0 : if (tdb_unlock(tdb, tlock->list, tlock->lock_rw) != 0)
128 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n"));
129 0 : return TDB_NEXT_LOCK_ERR;
130 : }
131 :
132 : /* traverse the entire database - calling fn(tdb, key, data) on each element.
133 : return -1 on error or the record count traversed
134 : if fn is NULL then it is not called
135 : a non-zero return value from fn() indicates that the traversal should stop
136 : */
137 4232308 : static int tdb_traverse_internal(struct tdb_context *tdb,
138 : tdb_traverse_func fn, void *private_data,
139 : struct tdb_traverse_lock *tl)
140 : {
141 : TDB_DATA key, dbuf;
142 : struct tdb_record rec;
143 4232308 : int ret = 0, count = 0;
144 : tdb_off_t off;
145 : size_t recbuf_len;
146 :
147 4232308 : recbuf_len = 4096;
148 4232308 : key.dptr = malloc(recbuf_len);
149 4232308 : if (key.dptr == NULL) {
150 0 : return -1;
151 : }
152 :
153 : /* This was in the initialization, above, but the IRIX compiler
154 : * did not like it. crh
155 : */
156 4232308 : tl->next = tdb->travlocks.next;
157 :
158 : /* fcntl locks don't stack: beware traverse inside traverse */
159 4232308 : tdb->travlocks.next = tl;
160 :
161 : /* tdb_next_lock places locks on the record returned, and its chain */
162 393967380 : while ((off = tdb_next_lock(tdb, tl, &rec)) != 0) {
163 : tdb_len_t full_len;
164 : int nread;
165 :
166 386163404 : if (off == TDB_NEXT_LOCK_ERR) {
167 0 : ret = -1;
168 0 : goto out;
169 : }
170 :
171 386163404 : full_len = rec.key_len + rec.data_len;
172 :
173 386163404 : if (full_len > recbuf_len) {
174 224368 : recbuf_len = full_len;
175 :
176 : /*
177 : * No realloc, we don't need the old data and thus can
178 : * do without the memcpy
179 : */
180 224368 : free(key.dptr);
181 224368 : key.dptr = malloc(recbuf_len);
182 :
183 224368 : if (key.dptr == NULL) {
184 0 : ret = -1;
185 0 : if (tdb_unlock(tdb, tl->list, tl->lock_rw)
186 : != 0) {
187 0 : goto out;
188 : }
189 0 : if (tdb_unlock_record(tdb, tl->off) != 0) {
190 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL,
191 : "tdb_traverse: malloc "
192 : "failed and unlock_record "
193 : "failed!\n"));
194 : }
195 0 : goto out;
196 : }
197 : }
198 :
199 386163404 : count++;
200 : /* now read the full record */
201 618544899 : nread = tdb->methods->tdb_read(tdb, tl->off + sizeof(rec),
202 386163404 : key.dptr, full_len, 0);
203 386163404 : if (nread == -1) {
204 0 : ret = -1;
205 0 : if (tdb_unlock(tdb, tl->list, tl->lock_rw) != 0)
206 0 : goto out;
207 0 : if (tdb_unlock_record(tdb, tl->off) != 0)
208 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
209 0 : goto out;
210 : }
211 386163404 : key.dsize = rec.key_len;
212 386163404 : dbuf.dptr = key.dptr + rec.key_len;
213 386163404 : dbuf.dsize = rec.data_len;
214 :
215 : tdb_trace_1rec_retrec(tdb, "traverse", key, dbuf);
216 :
217 : /* Drop chain lock, call out */
218 386163404 : if (tdb_unlock(tdb, tl->list, tl->lock_rw) != 0) {
219 0 : ret = -1;
220 0 : goto out;
221 : }
222 386163404 : if (fn && fn(tdb, key, dbuf, private_data)) {
223 : /* They want us to terminate traversal */
224 : tdb_trace_ret(tdb, "tdb_traverse_end", count);
225 37 : if (tdb_unlock_record(tdb, tl->off) != 0) {
226 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));;
227 0 : ret = -1;
228 : }
229 37 : goto out;
230 : }
231 : }
232 : tdb_trace(tdb, "tdb_traverse_end");
233 4232267 : out:
234 4232304 : SAFE_FREE(key.dptr);
235 4232304 : tdb->travlocks.next = tl->next;
236 4232304 : if (ret < 0)
237 0 : return -1;
238 : else
239 4232304 : return count;
240 : }
241 :
242 :
243 : /*
244 : a read style traverse - temporarily marks each record read only
245 : */
246 72319 : _PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb,
247 : tdb_traverse_func fn, void *private_data)
248 : {
249 72319 : struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK };
250 : int ret;
251 :
252 72319 : tdb->traverse_read++;
253 : tdb_trace(tdb, "tdb_traverse_read_start");
254 72319 : ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
255 72315 : tdb->traverse_read--;
256 :
257 72315 : return ret;
258 : }
259 :
260 : /*
261 : a write style traverse - needs to get the transaction lock to
262 : prevent deadlocks
263 :
264 : WARNING: The data buffer given to the callback fn does NOT meet the
265 : alignment guarantees malloc gives you.
266 : */
267 4160291 : _PUBLIC_ int tdb_traverse(struct tdb_context *tdb,
268 : tdb_traverse_func fn, void *private_data)
269 : {
270 4160291 : struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK };
271 : enum tdb_lock_flags lock_flags;
272 : int ret;
273 :
274 4160291 : if (tdb->read_only || tdb->traverse_read) {
275 300 : return tdb_traverse_read(tdb, fn, private_data);
276 : }
277 :
278 4159991 : lock_flags = TDB_LOCK_WAIT;
279 :
280 4159991 : if (tdb->allrecord_lock.count != 0) {
281 : /*
282 : * This avoids a deadlock between tdb_lockall() and
283 : * tdb_traverse(). See
284 : * https://bugzilla.samba.org/show_bug.cgi?id=11381
285 : */
286 41369 : lock_flags = TDB_LOCK_NOWAIT;
287 : }
288 :
289 4159991 : if (tdb_transaction_lock(tdb, F_WRLCK, lock_flags)) {
290 2 : return -1;
291 : }
292 :
293 4159989 : tdb->traverse_write++;
294 : tdb_trace(tdb, "tdb_traverse_start");
295 4159989 : ret = tdb_traverse_internal(tdb, fn, private_data, &tl);
296 4159989 : tdb->traverse_write--;
297 :
298 4159989 : tdb_transaction_unlock(tdb, F_WRLCK);
299 :
300 4159989 : return ret;
301 : }
302 :
303 :
304 : /* find the first entry in the database and return its key */
305 12 : _PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb)
306 : {
307 : TDB_DATA key;
308 : struct tdb_record rec;
309 : tdb_off_t off;
310 :
311 : /* release any old lock */
312 12 : if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0)
313 0 : return tdb_null;
314 12 : tdb->travlocks.off = tdb->travlocks.list = 0;
315 12 : tdb->travlocks.lock_rw = F_RDLCK;
316 :
317 : /* Grab first record: locks chain and returned record. */
318 12 : off = tdb_next_lock(tdb, &tdb->travlocks, &rec);
319 12 : if (off == 0 || off == TDB_NEXT_LOCK_ERR) {
320 : tdb_trace_retrec(tdb, "tdb_firstkey", tdb_null);
321 4 : return tdb_null;
322 : }
323 : /* now read the key */
324 8 : key.dsize = rec.key_len;
325 8 : key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
326 :
327 : tdb_trace_retrec(tdb, "tdb_firstkey", key);
328 :
329 : /* Unlock the hash chain of the record we just read. */
330 8 : if (tdb_unlock(tdb, tdb->travlocks.list, tdb->travlocks.lock_rw) != 0)
331 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
332 8 : return key;
333 : }
334 :
335 : /* find the next entry in the database, returning its key */
336 14 : _PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)
337 : {
338 : uint32_t oldlist;
339 14 : TDB_DATA key = tdb_null;
340 : struct tdb_record rec;
341 14 : unsigned char *k = NULL;
342 : tdb_off_t off;
343 :
344 : /* Is locked key the old key? If so, traverse will be reliable. */
345 14 : if (tdb->travlocks.off) {
346 14 : if (tdb_lock(tdb,tdb->travlocks.list,tdb->travlocks.lock_rw))
347 0 : return tdb_null;
348 14 : if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1
349 14 : || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
350 : rec.key_len))
351 14 : || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
352 : /* No, it wasn't: unlock it and start from scratch */
353 0 : if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) {
354 : tdb_trace_1rec_retrec(tdb, "tdb_nextkey",
355 : oldkey, tdb_null);
356 0 : SAFE_FREE(k);
357 0 : return tdb_null;
358 : }
359 0 : if (tdb_unlock(tdb, tdb->travlocks.list, tdb->travlocks.lock_rw) != 0) {
360 0 : SAFE_FREE(k);
361 0 : return tdb_null;
362 : }
363 0 : tdb->travlocks.off = 0;
364 : }
365 :
366 14 : SAFE_FREE(k);
367 : }
368 :
369 14 : if (!tdb->travlocks.off) {
370 : /* No previous element: do normal find, and lock record */
371 0 : tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec);
372 0 : if (!tdb->travlocks.off) {
373 : tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, tdb_null);
374 0 : return tdb_null;
375 : }
376 0 : tdb->travlocks.list = BUCKET(rec.full_hash);
377 0 : if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) {
378 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
379 0 : return tdb_null;
380 : }
381 : }
382 14 : oldlist = tdb->travlocks.list;
383 :
384 : /* Grab next record: locks chain and returned record,
385 : unlocks old record */
386 14 : off = tdb_next_lock(tdb, &tdb->travlocks, &rec);
387 14 : if (off != TDB_NEXT_LOCK_ERR && off != 0) {
388 6 : key.dsize = rec.key_len;
389 6 : key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
390 6 : key.dsize);
391 : /* Unlock the chain of this new record */
392 6 : if (tdb_unlock(tdb, tdb->travlocks.list, tdb->travlocks.lock_rw) != 0)
393 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
394 : }
395 : /* Unlock the chain of old record */
396 14 : if (tdb_unlock(tdb, oldlist, tdb->travlocks.lock_rw) != 0)
397 0 : TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
398 : tdb_trace_1rec_retrec(tdb, "tdb_nextkey", oldkey, key);
399 14 : return key;
400 : }
401 :
402 87305 : _PUBLIC_ int tdb_traverse_chain(struct tdb_context *tdb,
403 : unsigned chain,
404 : tdb_traverse_func fn,
405 : void *private_data)
406 : {
407 : tdb_off_t rec_ptr;
408 : struct tdb_chainwalk_ctx chainwalk;
409 87305 : int count = 0;
410 : int ret;
411 :
412 87305 : if (chain >= tdb->hash_size) {
413 0 : tdb->ecode = TDB_ERR_EINVAL;
414 0 : return -1;
415 : }
416 :
417 87305 : if (tdb->traverse_read != 0) {
418 0 : tdb->ecode = TDB_ERR_LOCK;
419 0 : return -1;
420 : }
421 :
422 87305 : ret = tdb_lock(tdb, chain, F_RDLCK);
423 87305 : if (ret == -1) {
424 0 : return -1;
425 : }
426 :
427 87305 : tdb->traverse_read += 1;
428 :
429 87305 : ret = tdb_ofs_read(tdb, TDB_HASH_TOP(chain), &rec_ptr);
430 87305 : if (ret == -1) {
431 0 : goto fail;
432 : }
433 :
434 87305 : tdb_chainwalk_init(&chainwalk, rec_ptr);
435 :
436 88963 : while (rec_ptr != 0) {
437 : struct tdb_record rec;
438 : bool ok;
439 :
440 8482 : ret = tdb_rec_read(tdb, rec_ptr, &rec);
441 8482 : if (ret == -1) {
442 0 : goto fail;
443 : }
444 :
445 8482 : if (!TDB_DEAD(&rec)) {
446 : /* no overflow checks, tdb_rec_read checked it */
447 8482 : tdb_off_t key_ofs = rec_ptr + sizeof(rec);
448 8482 : size_t full_len = rec.key_len + rec.data_len;
449 8482 : uint8_t *buf = NULL;
450 :
451 8482 : TDB_DATA key = { .dsize = rec.key_len };
452 8482 : TDB_DATA data = { .dsize = rec.data_len };
453 :
454 15306 : if ((tdb->transaction == NULL) &&
455 8482 : (tdb->map_ptr != NULL)) {
456 8482 : ret = tdb_oob(tdb, key_ofs, full_len, 0);
457 8482 : if (ret == -1) {
458 0 : goto fail;
459 : }
460 8482 : key.dptr = (uint8_t *)tdb->map_ptr + key_ofs;
461 : } else {
462 0 : buf = tdb_alloc_read(tdb, key_ofs, full_len);
463 0 : if (buf == NULL) {
464 0 : goto fail;
465 : }
466 0 : key.dptr = buf;
467 : }
468 8482 : data.dptr = key.dptr + key.dsize;
469 :
470 8482 : ret = fn(tdb, key, data, private_data);
471 8482 : free(buf);
472 :
473 8482 : count += 1;
474 :
475 8482 : if (ret != 0) {
476 0 : break;
477 : }
478 : }
479 :
480 8482 : rec_ptr = rec.next;
481 :
482 8482 : ok = tdb_chainwalk_check(tdb, &chainwalk, rec_ptr);
483 8482 : if (!ok) {
484 0 : goto fail;
485 : }
486 : }
487 87305 : tdb->traverse_read -= 1;
488 87305 : tdb_unlock(tdb, chain, F_RDLCK);
489 87305 : return count;
490 :
491 0 : fail:
492 0 : tdb->traverse_read -= 1;
493 0 : tdb_unlock(tdb, chain, F_RDLCK);
494 0 : return -1;
495 : }
496 :
497 87305 : _PUBLIC_ int tdb_traverse_key_chain(struct tdb_context *tdb,
498 : TDB_DATA key,
499 : tdb_traverse_func fn,
500 : void *private_data)
501 : {
502 : uint32_t hash, chain;
503 : int ret;
504 :
505 87305 : hash = tdb->hash_fn(&key);
506 87305 : chain = BUCKET(hash);
507 87305 : ret = tdb_traverse_chain(tdb, chain, fn, private_data);
508 :
509 87305 : return ret;
510 : }
|