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 ntdb
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/>.
29 #include <ccan/build_assert/build_assert.h>
31 /* If we were threaded, we could wait for unlock, but we're not, so fail. */
32 enum NTDB_ERROR
owner_conflict(struct ntdb_context
*ntdb
, const char *call
)
34 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
35 "%s: lock owned by another ntdb in this process.",
39 /* If we fork, we no longer really own locks. */
40 bool check_lock_pid(struct ntdb_context
*ntdb
, const char *call
, bool log
)
42 /* No locks? No problem! */
43 if (ntdb
->file
->allrecord_lock
.count
== 0
44 && ntdb
->file
->num_lockrecs
== 0) {
48 /* No fork? No problem! */
49 if (ntdb
->file
->locker
== getpid()) {
54 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
55 "%s: fork() detected after lock acquisition!"
56 " (%u vs %u)", call
, ntdb
->file
->locker
, getpid());
61 int ntdb_fcntl_lock(int fd
, int rw
, off_t off
, off_t len
, bool waitflag
,
69 fl
.l_whence
= SEEK_SET
;
74 ret
= fcntl(fd
, F_SETLKW
, &fl
);
76 ret
= fcntl(fd
, F_SETLK
, &fl
);
77 } while (ret
!= 0 && errno
== EINTR
);
81 int ntdb_fcntl_unlock(int fd
, int rw
, off_t off
, off_t len
, void *unused
)
88 fl
.l_whence
= SEEK_SET
;
92 ret
= fcntl(fd
, F_SETLKW
, &fl
);
93 } while (ret
!= 0 && errno
== EINTR
);
97 static int lock(struct ntdb_context
*ntdb
,
98 int rw
, off_t off
, off_t len
, bool waitflag
)
101 if (ntdb
->file
->allrecord_lock
.count
== 0
102 && ntdb
->file
->num_lockrecs
== 0) {
103 ntdb
->file
->locker
= getpid();
106 ntdb
->stats
.lock_lowlevel
++;
107 ret
= ntdb
->lock_fn(ntdb
->file
->fd
, rw
, off
, len
, waitflag
,
110 ntdb
->stats
.lock_nonblock
++;
112 ntdb
->stats
.lock_nonblock_fail
++;
117 static int unlock(struct ntdb_context
*ntdb
, int rw
, off_t off
, off_t len
)
119 #if 0 /* Check they matched up locks and unlocks correctly. */
124 locks
= fopen("/proc/locks", "r");
126 while (fgets(line
, 80, locks
)) {
130 /* eg. 1: FLOCK ADVISORY WRITE 2440 08:01:2180826 0 EOF */
131 p
= strchr(line
, ':') + 1;
132 if (strncmp(p
, " POSIX ADVISORY ", strlen(" POSIX ADVISORY ")))
134 p
+= strlen(" FLOCK ADVISORY ");
135 if (strncmp(p
, "READ ", strlen("READ ")) == 0)
137 else if (strncmp(p
, "WRITE ", strlen("WRITE ")) == 0)
142 if (atoi(p
) != getpid())
144 p
= strchr(strchr(p
, ' ') + 1, ' ') + 1;
146 p
= strchr(p
, ' ') + 1;
147 if (strncmp(p
, "EOF", 3) == 0)
150 l
= atoi(p
) - start
+ 1;
154 fprintf(stderr
, "Len %u should be %u: %s",
159 fprintf(stderr
, "Type %s wrong: %s",
160 rw
== F_RDLCK
? "READ" : "WRITE", line
);
169 fprintf(stderr
, "Unlock on %u@%u not found!",
177 return ntdb
->unlock_fn(ntdb
->file
->fd
, rw
, off
, len
, ntdb
->lock_data
);
180 /* a byte range locking function - return 0 on success
181 this functions locks len bytes at the specified offset.
183 note that a len of zero means lock to end of file
185 static enum NTDB_ERROR
ntdb_brlock(struct ntdb_context
*ntdb
,
186 int rw_type
, ntdb_off_t offset
, ntdb_off_t len
,
187 enum ntdb_lock_flags flags
)
191 if (rw_type
== F_WRLCK
&& (ntdb
->flags
& NTDB_RDONLY
)) {
192 return ntdb_logerr(ntdb
, NTDB_ERR_RDONLY
, NTDB_LOG_USE_ERROR
,
193 "Write lock attempted on read-only database");
196 if (ntdb
->flags
& NTDB_NOLOCK
) {
200 /* A 32 bit system cannot open a 64-bit file, but it could have
201 * expanded since then: check here. */
202 if ((size_t)(offset
+ len
) != offset
+ len
) {
203 return ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
204 "ntdb_brlock: lock on giant offset %llu",
205 (long long)(offset
+ len
));
208 ret
= lock(ntdb
, rw_type
, offset
, len
, flags
& NTDB_LOCK_WAIT
);
210 /* Generic lock error. errno set by fcntl.
211 * EAGAIN is an expected return from non-blocking
213 if (!(flags
& NTDB_LOCK_PROBE
)
214 && (errno
!= EAGAIN
&& errno
!= EINTR
)) {
215 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
216 "ntdb_brlock failed (fd=%d) at"
217 " offset %zu rw_type=%d flags=%d len=%zu:"
219 ntdb
->file
->fd
, (size_t)offset
, rw_type
,
220 flags
, (size_t)len
, strerror(errno
));
222 return NTDB_ERR_LOCK
;
227 static enum NTDB_ERROR
ntdb_brunlock(struct ntdb_context
*ntdb
,
228 int rw_type
, ntdb_off_t offset
, size_t len
)
230 if (ntdb
->flags
& NTDB_NOLOCK
) {
234 if (!check_lock_pid(ntdb
, "ntdb_brunlock", false))
235 return NTDB_ERR_LOCK
;
237 if (unlock(ntdb
, rw_type
, offset
, len
) == -1) {
238 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
239 "ntdb_brunlock failed (fd=%d) at offset %zu"
240 " rw_type=%d len=%zu: %s",
241 ntdb
->file
->fd
, (size_t)offset
, rw_type
,
242 (size_t)len
, strerror(errno
));
248 upgrade a read lock to a write lock. This needs to be handled in a
249 special way as some OSes (such as solaris) have too conservative
250 deadlock detection and claim a deadlock when progress can be
251 made. For those OSes we may loop for a while.
253 enum NTDB_ERROR
ntdb_allrecord_upgrade(struct ntdb_context
*ntdb
, off_t start
)
257 if (!check_lock_pid(ntdb
, "ntdb_transaction_prepare_commit", true))
258 return NTDB_ERR_LOCK
;
260 if (ntdb
->file
->allrecord_lock
.count
!= 1) {
261 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
262 "ntdb_allrecord_upgrade failed:"
263 " count %u too high",
264 ntdb
->file
->allrecord_lock
.count
);
267 if (ntdb
->file
->allrecord_lock
.off
!= 1) {
268 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
269 "ntdb_allrecord_upgrade failed:"
270 " already upgraded?");
273 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
274 return owner_conflict(ntdb
, "ntdb_allrecord_upgrade");
279 if (ntdb_brlock(ntdb
, F_WRLCK
, start
, 0,
280 NTDB_LOCK_WAIT
|NTDB_LOCK_PROBE
) == NTDB_SUCCESS
) {
281 ntdb
->file
->allrecord_lock
.ltype
= F_WRLCK
;
282 ntdb
->file
->allrecord_lock
.off
= 0;
285 if (errno
!= EDEADLK
) {
288 /* sleep for as short a time as we can - more portable than usleep() */
291 select(0, NULL
, NULL
, NULL
, &tv
);
294 if (errno
!= EAGAIN
&& errno
!= EINTR
)
295 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
296 "ntdb_allrecord_upgrade failed");
297 return NTDB_ERR_LOCK
;
300 static struct ntdb_lock
*find_nestlock(struct ntdb_context
*ntdb
, ntdb_off_t offset
,
301 const struct ntdb_context
*owner
)
305 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
306 if (ntdb
->file
->lockrecs
[i
].off
== offset
) {
307 if (owner
&& ntdb
->file
->lockrecs
[i
].owner
!= owner
)
309 return &ntdb
->file
->lockrecs
[i
];
315 enum NTDB_ERROR
ntdb_lock_and_recover(struct ntdb_context
*ntdb
)
317 enum NTDB_ERROR ecode
;
319 if (!check_lock_pid(ntdb
, "ntdb_transaction_prepare_commit", true))
320 return NTDB_ERR_LOCK
;
322 ecode
= ntdb_allrecord_lock(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
,
324 if (ecode
!= NTDB_SUCCESS
) {
328 ecode
= ntdb_lock_open(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
);
329 if (ecode
!= NTDB_SUCCESS
) {
330 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
333 ecode
= ntdb_transaction_recover(ntdb
);
334 ntdb_unlock_open(ntdb
, F_WRLCK
);
335 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
340 /* lock an offset in the database. */
341 static enum NTDB_ERROR
ntdb_nest_lock(struct ntdb_context
*ntdb
,
342 ntdb_off_t offset
, int ltype
,
343 enum ntdb_lock_flags flags
)
345 struct ntdb_lock
*new_lck
;
346 enum NTDB_ERROR ecode
;
348 assert(offset
<= (NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
)
349 + ntdb
->file
->map_size
/ 8));
351 if (ntdb
->flags
& NTDB_NOLOCK
)
354 if (!check_lock_pid(ntdb
, "ntdb_nest_lock", true)) {
355 return NTDB_ERR_LOCK
;
360 new_lck
= find_nestlock(ntdb
, offset
, NULL
);
362 if (new_lck
->owner
!= ntdb
) {
363 return owner_conflict(ntdb
, "ntdb_nest_lock");
366 if (new_lck
->ltype
== F_RDLCK
&& ltype
== F_WRLCK
) {
367 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
369 " offset %zu has read lock",
372 /* Just increment the struct, posix locks don't stack. */
378 if (ntdb
->file
->num_lockrecs
379 && offset
>= NTDB_HASH_LOCK_START
380 && offset
< NTDB_HASH_LOCK_START
+ NTDB_HASH_LOCK_RANGE
) {
381 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
382 "ntdb_nest_lock: already have a hash lock?");
385 if (ntdb
->file
->lockrecs
== NULL
) {
386 new_lck
= ntdb
->alloc_fn(ntdb
->file
, sizeof(*ntdb
->file
->lockrecs
),
389 new_lck
= (struct ntdb_lock
*)ntdb
->expand_fn(
390 ntdb
->file
->lockrecs
,
391 sizeof(*ntdb
->file
->lockrecs
)
392 * (ntdb
->file
->num_lockrecs
+1),
395 if (new_lck
== NULL
) {
396 return ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
398 " unable to allocate %zu lock struct",
399 ntdb
->file
->num_lockrecs
+ 1);
401 ntdb
->file
->lockrecs
= new_lck
;
403 /* Since fcntl locks don't nest, we do a lock for the first one,
404 and simply bump the count for future ones */
405 ecode
= ntdb_brlock(ntdb
, ltype
, offset
, 1, flags
);
406 if (ecode
!= NTDB_SUCCESS
) {
410 /* First time we grab a lock, perhaps someone died in commit? */
411 if (!(flags
& NTDB_LOCK_NOCHECK
)
412 && ntdb
->file
->num_lockrecs
== 0) {
413 ntdb_bool_err berr
= ntdb_needs_recovery(ntdb
);
415 ntdb_brunlock(ntdb
, ltype
, offset
, 1);
418 return NTDB_OFF_TO_ERR(berr
);
419 ecode
= ntdb_lock_and_recover(ntdb
);
420 if (ecode
== NTDB_SUCCESS
) {
421 ecode
= ntdb_brlock(ntdb
, ltype
, offset
, 1,
424 if (ecode
!= NTDB_SUCCESS
) {
430 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].owner
= ntdb
;
431 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].off
= offset
;
432 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].count
= 1;
433 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].ltype
= ltype
;
434 ntdb
->file
->num_lockrecs
++;
439 static enum NTDB_ERROR
ntdb_nest_unlock(struct ntdb_context
*ntdb
,
440 ntdb_off_t off
, int ltype
)
442 struct ntdb_lock
*lck
;
443 enum NTDB_ERROR ecode
;
445 if (ntdb
->flags
& NTDB_NOLOCK
)
448 lck
= find_nestlock(ntdb
, off
, ntdb
);
449 if ((lck
== NULL
) || (lck
->count
== 0)) {
450 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
451 "ntdb_nest_unlock: no lock for %zu",
455 if (lck
->count
> 1) {
461 * This lock has count==1 left, so we need to unlock it in the
462 * kernel. We don't bother with decrementing the in-memory array
463 * element, we're about to overwrite it with the last array element
466 ecode
= ntdb_brunlock(ntdb
, ltype
, off
, 1);
469 * Shrink the array by overwriting the element just unlocked with the
470 * last array element.
472 *lck
= ntdb
->file
->lockrecs
[--ntdb
->file
->num_lockrecs
];
478 get the transaction lock
480 enum NTDB_ERROR
ntdb_transaction_lock(struct ntdb_context
*ntdb
, int ltype
)
482 return ntdb_nest_lock(ntdb
, NTDB_TRANSACTION_LOCK
, ltype
, NTDB_LOCK_WAIT
);
486 release the transaction lock
488 void ntdb_transaction_unlock(struct ntdb_context
*ntdb
, int ltype
)
490 ntdb_nest_unlock(ntdb
, NTDB_TRANSACTION_LOCK
, ltype
);
493 /* We only need to lock individual bytes, but Linux merges consecutive locks
494 * so we lock in contiguous ranges. */
495 static enum NTDB_ERROR
ntdb_lock_gradual(struct ntdb_context
*ntdb
,
496 int ltype
, enum ntdb_lock_flags flags
,
497 ntdb_off_t off
, ntdb_off_t len
)
499 enum NTDB_ERROR ecode
;
500 enum ntdb_lock_flags nb_flags
= (flags
& ~NTDB_LOCK_WAIT
);
503 /* 0 would mean to end-of-file... */
505 /* Single hash. Just do blocking lock. */
506 return ntdb_brlock(ntdb
, ltype
, off
, len
, flags
);
509 /* First we try non-blocking. */
510 ecode
= ntdb_brlock(ntdb
, ltype
, off
, len
, nb_flags
);
511 if (ecode
!= NTDB_ERR_LOCK
) {
515 /* Try locking first half, then second. */
516 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
, off
, len
/ 2);
517 if (ecode
!= NTDB_SUCCESS
)
520 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
,
521 off
+ len
/ 2, len
- len
/ 2);
522 if (ecode
!= NTDB_SUCCESS
) {
523 ntdb_brunlock(ntdb
, ltype
, off
, len
/ 2);
528 /* lock/unlock entire database. It can only be upgradable if you have some
529 * other way of guaranteeing exclusivity (ie. transaction write lock). */
530 enum NTDB_ERROR
ntdb_allrecord_lock(struct ntdb_context
*ntdb
, int ltype
,
531 enum ntdb_lock_flags flags
, bool upgradable
)
533 enum NTDB_ERROR ecode
;
536 if (ntdb
->flags
& NTDB_NOLOCK
) {
540 if (!check_lock_pid(ntdb
, "ntdb_allrecord_lock", true)) {
541 return NTDB_ERR_LOCK
;
544 if (ntdb
->file
->allrecord_lock
.count
) {
545 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
546 return owner_conflict(ntdb
, "ntdb_allrecord_lock");
550 || ntdb
->file
->allrecord_lock
.ltype
== F_WRLCK
) {
551 ntdb
->file
->allrecord_lock
.count
++;
555 /* a global lock of a different type exists */
556 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
557 "ntdb_allrecord_lock: already have %s lock",
558 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
562 if (ntdb_has_hash_locks(ntdb
)) {
563 /* can't combine global and chain locks */
564 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
565 "ntdb_allrecord_lock:"
566 " already have chain lock");
569 if (upgradable
&& ltype
!= F_RDLCK
) {
570 /* ntdb error: you can't upgrade a write lock! */
571 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
572 "ntdb_allrecord_lock:"
573 " can't upgrade a write lock");
578 /* Lock hashes, gradually. */
579 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
, NTDB_HASH_LOCK_START
,
580 1 << ntdb
->hash_bits
);
581 if (ecode
!= NTDB_SUCCESS
)
584 /* Lock free tables: there to end of file. */
585 ecode
= ntdb_brlock(ntdb
, ltype
,
586 NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
),
588 if (ecode
!= NTDB_SUCCESS
) {
589 ntdb_brunlock(ntdb
, ltype
, NTDB_HASH_LOCK_START
,
590 1 << ntdb
->hash_bits
);
594 ntdb
->file
->allrecord_lock
.owner
= ntdb
;
595 ntdb
->file
->allrecord_lock
.count
= 1;
596 /* If it's upgradable, it's actually exclusive so we can treat
597 * it as a write lock. */
598 ntdb
->file
->allrecord_lock
.ltype
= upgradable
? F_WRLCK
: ltype
;
599 ntdb
->file
->allrecord_lock
.off
= upgradable
;
601 /* Now check for needing recovery. */
602 if (flags
& NTDB_LOCK_NOCHECK
)
605 berr
= ntdb_needs_recovery(ntdb
);
606 if (likely(berr
== false))
609 ntdb_allrecord_unlock(ntdb
, ltype
);
611 return NTDB_OFF_TO_ERR(berr
);
612 ecode
= ntdb_lock_and_recover(ntdb
);
613 if (ecode
!= NTDB_SUCCESS
) {
619 enum NTDB_ERROR
ntdb_lock_open(struct ntdb_context
*ntdb
,
620 int ltype
, enum ntdb_lock_flags flags
)
622 return ntdb_nest_lock(ntdb
, NTDB_OPEN_LOCK
, ltype
, flags
);
625 void ntdb_unlock_open(struct ntdb_context
*ntdb
, int ltype
)
627 ntdb_nest_unlock(ntdb
, NTDB_OPEN_LOCK
, ltype
);
630 bool ntdb_has_open_lock(struct ntdb_context
*ntdb
)
632 return !(ntdb
->flags
& NTDB_NOLOCK
)
633 && find_nestlock(ntdb
, NTDB_OPEN_LOCK
, ntdb
) != NULL
;
636 enum NTDB_ERROR
ntdb_lock_expand(struct ntdb_context
*ntdb
, int ltype
)
638 /* Lock doesn't protect data, so don't check (we recurse if we do!) */
639 return ntdb_nest_lock(ntdb
, NTDB_EXPANSION_LOCK
, ltype
,
640 NTDB_LOCK_WAIT
| NTDB_LOCK_NOCHECK
);
643 void ntdb_unlock_expand(struct ntdb_context
*ntdb
, int ltype
)
645 ntdb_nest_unlock(ntdb
, NTDB_EXPANSION_LOCK
, ltype
);
648 /* unlock entire db */
649 void ntdb_allrecord_unlock(struct ntdb_context
*ntdb
, int ltype
)
651 if (ntdb
->flags
& NTDB_NOLOCK
)
654 if (ntdb
->file
->allrecord_lock
.count
== 0) {
655 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
656 "ntdb_allrecord_unlock: not locked!");
660 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
661 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
662 "ntdb_allrecord_unlock: not locked by us!");
666 /* Upgradable locks are marked as write locks. */
667 if (ntdb
->file
->allrecord_lock
.ltype
!= ltype
668 && (!ntdb
->file
->allrecord_lock
.off
|| ltype
!= F_RDLCK
)) {
669 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
670 "ntdb_allrecord_unlock: have %s lock",
671 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
676 if (ntdb
->file
->allrecord_lock
.count
> 1) {
677 ntdb
->file
->allrecord_lock
.count
--;
681 ntdb
->file
->allrecord_lock
.count
= 0;
682 ntdb
->file
->allrecord_lock
.ltype
= 0;
684 ntdb_brunlock(ntdb
, ltype
, NTDB_HASH_LOCK_START
, 0);
687 bool ntdb_has_expansion_lock(struct ntdb_context
*ntdb
)
689 return find_nestlock(ntdb
, NTDB_EXPANSION_LOCK
, ntdb
) != NULL
;
692 bool ntdb_has_hash_locks(struct ntdb_context
*ntdb
)
696 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
697 if (ntdb
->file
->lockrecs
[i
].off
>= NTDB_HASH_LOCK_START
698 && ntdb
->file
->lockrecs
[i
].off
< (NTDB_HASH_LOCK_START
699 + (1 << ntdb
->hash_bits
)))
705 static bool ntdb_has_free_lock(struct ntdb_context
*ntdb
)
709 if (ntdb
->flags
& NTDB_NOLOCK
)
712 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
713 if (ntdb
->file
->lockrecs
[i
].off
714 > NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
))
720 enum NTDB_ERROR
ntdb_lock_hash(struct ntdb_context
*ntdb
,
724 unsigned l
= NTDB_HASH_LOCK_START
+ h
;
726 assert(h
< (1 << ntdb
->hash_bits
));
728 /* a allrecord lock allows us to avoid per chain locks */
729 if (ntdb
->file
->allrecord_lock
.count
) {
730 if (!check_lock_pid(ntdb
, "ntdb_lock_hashes", true))
731 return NTDB_ERR_LOCK
;
733 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
)
734 return owner_conflict(ntdb
, "ntdb_lock_hashes");
735 if (ltype
== ntdb
->file
->allrecord_lock
.ltype
736 || ltype
== F_RDLCK
) {
740 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
742 " already have %s allrecordlock",
743 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
747 if (ntdb_has_free_lock(ntdb
)) {
748 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
749 "ntdb_lock_hashes: already have free lock");
752 if (ntdb_has_expansion_lock(ntdb
)) {
753 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
755 " already have expansion lock");
758 return ntdb_nest_lock(ntdb
, l
, ltype
, NTDB_LOCK_WAIT
);
761 enum NTDB_ERROR
ntdb_unlock_hash(struct ntdb_context
*ntdb
,
762 unsigned int h
, int ltype
)
764 unsigned l
= NTDB_HASH_LOCK_START
+ (h
& ((1 << ntdb
->hash_bits
)-1));
766 if (ntdb
->flags
& NTDB_NOLOCK
)
769 /* a allrecord lock allows us to avoid per chain locks */
770 if (ntdb
->file
->allrecord_lock
.count
) {
771 if (ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
772 && ltype
== F_WRLCK
) {
773 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
774 "ntdb_unlock_hashes RO allrecord!");
776 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
777 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
778 "ntdb_unlock_hashes:"
779 " not locked by us!");
784 return ntdb_nest_unlock(ntdb
, l
, ltype
);
787 /* Hash locks use NTDB_HASH_LOCK_START + <number of hash entries>..
788 * Then we begin; bucket offsets are sizeof(ntdb_len_t) apart, so we divide.
789 * The result is that on 32 bit systems we don't use lock values > 2^31 on
790 * files that are less than 4GB.
792 static ntdb_off_t
free_lock_off(const struct ntdb_context
*ntdb
,
795 return NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
)
796 + b_off
/ sizeof(ntdb_off_t
);
799 enum NTDB_ERROR
ntdb_lock_free_bucket(struct ntdb_context
*ntdb
, ntdb_off_t b_off
,
800 enum ntdb_lock_flags waitflag
)
802 assert(b_off
>= sizeof(struct ntdb_header
));
804 if (ntdb
->flags
& NTDB_NOLOCK
)
807 /* a allrecord lock allows us to avoid per chain locks */
808 if (ntdb
->file
->allrecord_lock
.count
) {
809 if (!check_lock_pid(ntdb
, "ntdb_lock_free_bucket", true))
810 return NTDB_ERR_LOCK
;
812 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
813 return owner_conflict(ntdb
, "ntdb_lock_free_bucket");
816 if (ntdb
->file
->allrecord_lock
.ltype
== F_WRLCK
)
818 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
819 "ntdb_lock_free_bucket with"
820 " read-only allrecordlock!");
824 if (ntdb_has_expansion_lock(ntdb
)) {
825 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
826 "ntdb_lock_free_bucket:"
827 " already have expansion lock");
831 return ntdb_nest_lock(ntdb
, free_lock_off(ntdb
, b_off
), F_WRLCK
,
835 void ntdb_unlock_free_bucket(struct ntdb_context
*ntdb
, ntdb_off_t b_off
)
837 if (ntdb
->file
->allrecord_lock
.count
)
840 ntdb_nest_unlock(ntdb
, free_lock_off(ntdb
, b_off
), F_WRLCK
);
843 _PUBLIC_
enum NTDB_ERROR
ntdb_lockall(struct ntdb_context
*ntdb
)
845 return ntdb_allrecord_lock(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
, false);
848 _PUBLIC_
void ntdb_unlockall(struct ntdb_context
*ntdb
)
850 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
853 _PUBLIC_
enum NTDB_ERROR
ntdb_lockall_read(struct ntdb_context
*ntdb
)
855 return ntdb_allrecord_lock(ntdb
, F_RDLCK
, NTDB_LOCK_WAIT
, false);
858 _PUBLIC_
void ntdb_unlockall_read(struct ntdb_context
*ntdb
)
860 ntdb_allrecord_unlock(ntdb
, F_RDLCK
);
863 void ntdb_lock_cleanup(struct ntdb_context
*ntdb
)
867 /* We don't want to warn: they're allowed to close ntdb after fork. */
868 if (!check_lock_pid(ntdb
, "ntdb_close", false))
871 while (ntdb
->file
->allrecord_lock
.count
872 && ntdb
->file
->allrecord_lock
.owner
== ntdb
) {
873 ntdb_allrecord_unlock(ntdb
, ntdb
->file
->allrecord_lock
.ltype
);
876 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
877 if (ntdb
->file
->lockrecs
[i
].owner
== ntdb
) {
878 ntdb_nest_unlock(ntdb
,
879 ntdb
->file
->lockrecs
[i
].off
,
880 ntdb
->file
->lockrecs
[i
].ltype
);