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!"
57 (unsigned int)ntdb
->file
->locker
,
58 (unsigned int)getpid());
63 int ntdb_fcntl_lock(int fd
, int rw
, off_t off
, off_t len
, bool waitflag
,
71 fl
.l_whence
= SEEK_SET
;
76 ret
= fcntl(fd
, F_SETLKW
, &fl
);
78 ret
= fcntl(fd
, F_SETLK
, &fl
);
79 } while (ret
!= 0 && errno
== EINTR
);
83 int ntdb_fcntl_unlock(int fd
, int rw
, off_t off
, off_t len
, void *unused
)
90 fl
.l_whence
= SEEK_SET
;
94 ret
= fcntl(fd
, F_SETLKW
, &fl
);
95 } while (ret
!= 0 && errno
== EINTR
);
99 static int lock(struct ntdb_context
*ntdb
,
100 int rw
, off_t off
, off_t len
, bool waitflag
)
103 if (ntdb
->file
->allrecord_lock
.count
== 0
104 && ntdb
->file
->num_lockrecs
== 0) {
105 ntdb
->file
->locker
= getpid();
108 ntdb
->stats
.lock_lowlevel
++;
109 ret
= ntdb
->lock_fn(ntdb
->file
->fd
, rw
, off
, len
, waitflag
,
112 ntdb
->stats
.lock_nonblock
++;
114 ntdb
->stats
.lock_nonblock_fail
++;
119 static int unlock(struct ntdb_context
*ntdb
, int rw
, off_t off
, off_t len
)
121 #if 0 /* Check they matched up locks and unlocks correctly. */
126 locks
= fopen("/proc/locks", "r");
128 while (fgets(line
, 80, locks
)) {
132 /* eg. 1: FLOCK ADVISORY WRITE 2440 08:01:2180826 0 EOF */
133 p
= strchr(line
, ':') + 1;
134 if (strncmp(p
, " POSIX ADVISORY ", strlen(" POSIX ADVISORY ")))
136 p
+= strlen(" FLOCK ADVISORY ");
137 if (strncmp(p
, "READ ", strlen("READ ")) == 0)
139 else if (strncmp(p
, "WRITE ", strlen("WRITE ")) == 0)
144 if (atoi(p
) != getpid())
146 p
= strchr(strchr(p
, ' ') + 1, ' ') + 1;
148 p
= strchr(p
, ' ') + 1;
149 if (strncmp(p
, "EOF", 3) == 0)
152 l
= atoi(p
) - start
+ 1;
156 fprintf(stderr
, "Len %u should be %u: %s",
161 fprintf(stderr
, "Type %s wrong: %s",
162 rw
== F_RDLCK
? "READ" : "WRITE", line
);
171 fprintf(stderr
, "Unlock on %u@%u not found!",
179 return ntdb
->unlock_fn(ntdb
->file
->fd
, rw
, off
, len
, ntdb
->lock_data
);
182 /* a byte range locking function - return 0 on success
183 this functions locks len bytes at the specified offset.
185 note that a len of zero means lock to end of file
187 static enum NTDB_ERROR
ntdb_brlock(struct ntdb_context
*ntdb
,
188 int rw_type
, ntdb_off_t offset
, ntdb_off_t len
,
189 enum ntdb_lock_flags flags
)
193 if (rw_type
== F_WRLCK
&& (ntdb
->flags
& NTDB_RDONLY
)) {
194 return ntdb_logerr(ntdb
, NTDB_ERR_RDONLY
, NTDB_LOG_USE_ERROR
,
195 "Write lock attempted on read-only database");
198 if (ntdb
->flags
& NTDB_NOLOCK
) {
202 /* A 32 bit system cannot open a 64-bit file, but it could have
203 * expanded since then: check here. */
204 if ((size_t)(offset
+ len
) != offset
+ len
) {
205 return ntdb_logerr(ntdb
, NTDB_ERR_IO
, NTDB_LOG_ERROR
,
206 "ntdb_brlock: lock on giant offset %llu",
207 (long long)(offset
+ len
));
210 ret
= lock(ntdb
, rw_type
, offset
, len
, flags
& NTDB_LOCK_WAIT
);
212 /* Generic lock error. errno set by fcntl.
213 * EAGAIN is an expected return from non-blocking
215 if (!(flags
& NTDB_LOCK_PROBE
)
216 && (errno
!= EAGAIN
&& errno
!= EINTR
)) {
217 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
218 "ntdb_brlock failed (fd=%d) at"
219 " offset %zu rw_type=%d flags=%d len=%zu:"
221 ntdb
->file
->fd
, (size_t)offset
, rw_type
,
222 flags
, (size_t)len
, strerror(errno
));
224 return NTDB_ERR_LOCK
;
229 static enum NTDB_ERROR
ntdb_brunlock(struct ntdb_context
*ntdb
,
230 int rw_type
, ntdb_off_t offset
, size_t len
)
232 if (ntdb
->flags
& NTDB_NOLOCK
) {
236 if (!check_lock_pid(ntdb
, "ntdb_brunlock", false))
237 return NTDB_ERR_LOCK
;
239 if (unlock(ntdb
, rw_type
, offset
, len
) == -1) {
240 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
241 "ntdb_brunlock failed (fd=%d) at offset %zu"
242 " rw_type=%d len=%zu: %s",
243 ntdb
->file
->fd
, (size_t)offset
, rw_type
,
244 (size_t)len
, strerror(errno
));
250 upgrade a read lock to a write lock. This needs to be handled in a
251 special way as some OSes (such as solaris) have too conservative
252 deadlock detection and claim a deadlock when progress can be
253 made. For those OSes we may loop for a while.
255 enum NTDB_ERROR
ntdb_allrecord_upgrade(struct ntdb_context
*ntdb
, off_t start
)
259 if (!check_lock_pid(ntdb
, "ntdb_transaction_prepare_commit", true))
260 return NTDB_ERR_LOCK
;
262 if (ntdb
->file
->allrecord_lock
.count
!= 1) {
263 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
264 "ntdb_allrecord_upgrade failed:"
265 " count %u too high",
266 ntdb
->file
->allrecord_lock
.count
);
269 if (ntdb
->file
->allrecord_lock
.off
!= 1) {
270 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
271 "ntdb_allrecord_upgrade failed:"
272 " already upgraded?");
275 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
276 return owner_conflict(ntdb
, "ntdb_allrecord_upgrade");
281 if (ntdb_brlock(ntdb
, F_WRLCK
, start
, 0,
282 NTDB_LOCK_WAIT
|NTDB_LOCK_PROBE
) == NTDB_SUCCESS
) {
283 ntdb
->file
->allrecord_lock
.ltype
= F_WRLCK
;
284 ntdb
->file
->allrecord_lock
.off
= 0;
287 if (errno
!= EDEADLK
) {
290 /* sleep for as short a time as we can - more portable than usleep() */
293 select(0, NULL
, NULL
, NULL
, &tv
);
296 if (errno
!= EAGAIN
&& errno
!= EINTR
)
297 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
298 "ntdb_allrecord_upgrade failed");
299 return NTDB_ERR_LOCK
;
302 static struct ntdb_lock
*find_nestlock(struct ntdb_context
*ntdb
, ntdb_off_t offset
,
303 const struct ntdb_context
*owner
)
307 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
308 if (ntdb
->file
->lockrecs
[i
].off
== offset
) {
309 if (owner
&& ntdb
->file
->lockrecs
[i
].owner
!= owner
)
311 return &ntdb
->file
->lockrecs
[i
];
317 enum NTDB_ERROR
ntdb_lock_and_recover(struct ntdb_context
*ntdb
)
319 enum NTDB_ERROR ecode
;
321 if (!check_lock_pid(ntdb
, "ntdb_transaction_prepare_commit", true))
322 return NTDB_ERR_LOCK
;
324 ecode
= ntdb_allrecord_lock(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
,
326 if (ecode
!= NTDB_SUCCESS
) {
330 ecode
= ntdb_lock_open(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
|NTDB_LOCK_NOCHECK
);
331 if (ecode
!= NTDB_SUCCESS
) {
332 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
335 ecode
= ntdb_transaction_recover(ntdb
);
336 ntdb_unlock_open(ntdb
, F_WRLCK
);
337 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
342 /* lock an offset in the database. */
343 static enum NTDB_ERROR
ntdb_nest_lock(struct ntdb_context
*ntdb
,
344 ntdb_off_t offset
, int ltype
,
345 enum ntdb_lock_flags flags
)
347 struct ntdb_lock
*new_lck
;
348 enum NTDB_ERROR ecode
;
350 assert(offset
<= (NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
)
351 + ntdb
->file
->map_size
/ 8));
353 if (ntdb
->flags
& NTDB_NOLOCK
)
356 if (!check_lock_pid(ntdb
, "ntdb_nest_lock", true)) {
357 return NTDB_ERR_LOCK
;
362 new_lck
= find_nestlock(ntdb
, offset
, NULL
);
364 if (new_lck
->owner
!= ntdb
) {
365 return owner_conflict(ntdb
, "ntdb_nest_lock");
368 if (new_lck
->ltype
== F_RDLCK
&& ltype
== F_WRLCK
) {
369 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
371 " offset %zu has read lock",
374 /* Just increment the struct, posix locks don't stack. */
380 if (ntdb
->file
->num_lockrecs
381 && offset
>= NTDB_HASH_LOCK_START
382 && offset
< NTDB_HASH_LOCK_START
+ NTDB_HASH_LOCK_RANGE
) {
383 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
384 "ntdb_nest_lock: already have a hash lock?");
387 if (ntdb
->file
->lockrecs
== NULL
) {
388 new_lck
= ntdb
->alloc_fn(ntdb
->file
, sizeof(*ntdb
->file
->lockrecs
),
391 new_lck
= (struct ntdb_lock
*)ntdb
->expand_fn(
392 ntdb
->file
->lockrecs
,
393 sizeof(*ntdb
->file
->lockrecs
)
394 * (ntdb
->file
->num_lockrecs
+1),
397 if (new_lck
== NULL
) {
398 return ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
400 " unable to allocate %zu lock struct",
401 ntdb
->file
->num_lockrecs
+ 1);
403 ntdb
->file
->lockrecs
= new_lck
;
405 /* Since fcntl locks don't nest, we do a lock for the first one,
406 and simply bump the count for future ones */
407 ecode
= ntdb_brlock(ntdb
, ltype
, offset
, 1, flags
);
408 if (ecode
!= NTDB_SUCCESS
) {
412 /* First time we grab a lock, perhaps someone died in commit? */
413 if (!(flags
& NTDB_LOCK_NOCHECK
)
414 && ntdb
->file
->num_lockrecs
== 0) {
415 ntdb_bool_err berr
= ntdb_needs_recovery(ntdb
);
417 ntdb_brunlock(ntdb
, ltype
, offset
, 1);
420 return NTDB_OFF_TO_ERR(berr
);
421 ecode
= ntdb_lock_and_recover(ntdb
);
422 if (ecode
== NTDB_SUCCESS
) {
423 ecode
= ntdb_brlock(ntdb
, ltype
, offset
, 1,
426 if (ecode
!= NTDB_SUCCESS
) {
432 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].owner
= ntdb
;
433 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].off
= offset
;
434 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].count
= 1;
435 ntdb
->file
->lockrecs
[ntdb
->file
->num_lockrecs
].ltype
= ltype
;
436 ntdb
->file
->num_lockrecs
++;
441 static enum NTDB_ERROR
ntdb_nest_unlock(struct ntdb_context
*ntdb
,
442 ntdb_off_t off
, int ltype
)
444 struct ntdb_lock
*lck
;
445 enum NTDB_ERROR ecode
;
447 if (ntdb
->flags
& NTDB_NOLOCK
)
450 lck
= find_nestlock(ntdb
, off
, ntdb
);
451 if ((lck
== NULL
) || (lck
->count
== 0)) {
452 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
453 "ntdb_nest_unlock: no lock for %zu",
457 if (lck
->count
> 1) {
463 * This lock has count==1 left, so we need to unlock it in the
464 * kernel. We don't bother with decrementing the in-memory array
465 * element, we're about to overwrite it with the last array element
468 ecode
= ntdb_brunlock(ntdb
, ltype
, off
, 1);
471 * Shrink the array by overwriting the element just unlocked with the
472 * last array element.
474 *lck
= ntdb
->file
->lockrecs
[--ntdb
->file
->num_lockrecs
];
480 get the transaction lock
482 enum NTDB_ERROR
ntdb_transaction_lock(struct ntdb_context
*ntdb
, int ltype
)
484 return ntdb_nest_lock(ntdb
, NTDB_TRANSACTION_LOCK
, ltype
, NTDB_LOCK_WAIT
);
488 release the transaction lock
490 void ntdb_transaction_unlock(struct ntdb_context
*ntdb
, int ltype
)
492 ntdb_nest_unlock(ntdb
, NTDB_TRANSACTION_LOCK
, ltype
);
495 /* We only need to lock individual bytes, but Linux merges consecutive locks
496 * so we lock in contiguous ranges. */
497 static enum NTDB_ERROR
ntdb_lock_gradual(struct ntdb_context
*ntdb
,
498 int ltype
, enum ntdb_lock_flags flags
,
499 ntdb_off_t off
, ntdb_off_t len
)
501 enum NTDB_ERROR ecode
;
502 enum ntdb_lock_flags nb_flags
= (flags
& ~NTDB_LOCK_WAIT
);
505 /* 0 would mean to end-of-file... */
507 /* Single hash. Just do blocking lock. */
508 return ntdb_brlock(ntdb
, ltype
, off
, len
, flags
);
511 /* First we try non-blocking. */
512 ecode
= ntdb_brlock(ntdb
, ltype
, off
, len
, nb_flags
);
513 if (ecode
!= NTDB_ERR_LOCK
) {
517 /* Try locking first half, then second. */
518 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
, off
, len
/ 2);
519 if (ecode
!= NTDB_SUCCESS
)
522 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
,
523 off
+ len
/ 2, len
- len
/ 2);
524 if (ecode
!= NTDB_SUCCESS
) {
525 ntdb_brunlock(ntdb
, ltype
, off
, len
/ 2);
530 /* lock/unlock entire database. It can only be upgradable if you have some
531 * other way of guaranteeing exclusivity (ie. transaction write lock). */
532 enum NTDB_ERROR
ntdb_allrecord_lock(struct ntdb_context
*ntdb
, int ltype
,
533 enum ntdb_lock_flags flags
, bool upgradable
)
535 enum NTDB_ERROR ecode
;
538 if (ntdb
->flags
& NTDB_NOLOCK
) {
542 if (!check_lock_pid(ntdb
, "ntdb_allrecord_lock", true)) {
543 return NTDB_ERR_LOCK
;
546 if (ntdb
->file
->allrecord_lock
.count
) {
547 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
548 return owner_conflict(ntdb
, "ntdb_allrecord_lock");
552 || ntdb
->file
->allrecord_lock
.ltype
== F_WRLCK
) {
553 ntdb
->file
->allrecord_lock
.count
++;
557 /* a global lock of a different type exists */
558 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
559 "ntdb_allrecord_lock: already have %s lock",
560 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
564 if (ntdb_has_hash_locks(ntdb
)) {
565 /* can't combine global and chain locks */
566 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
567 "ntdb_allrecord_lock:"
568 " already have chain lock");
571 if (upgradable
&& ltype
!= F_RDLCK
) {
572 /* ntdb error: you can't upgrade a write lock! */
573 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
574 "ntdb_allrecord_lock:"
575 " can't upgrade a write lock");
580 /* Lock hashes, gradually. */
581 ecode
= ntdb_lock_gradual(ntdb
, ltype
, flags
, NTDB_HASH_LOCK_START
,
582 1 << ntdb
->hash_bits
);
583 if (ecode
!= NTDB_SUCCESS
)
586 /* Lock free tables: there to end of file. */
587 ecode
= ntdb_brlock(ntdb
, ltype
,
588 NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
),
590 if (ecode
!= NTDB_SUCCESS
) {
591 ntdb_brunlock(ntdb
, ltype
, NTDB_HASH_LOCK_START
,
592 1 << ntdb
->hash_bits
);
596 ntdb
->file
->allrecord_lock
.owner
= ntdb
;
597 ntdb
->file
->allrecord_lock
.count
= 1;
598 /* If it's upgradable, it's actually exclusive so we can treat
599 * it as a write lock. */
600 ntdb
->file
->allrecord_lock
.ltype
= upgradable
? F_WRLCK
: ltype
;
601 ntdb
->file
->allrecord_lock
.off
= upgradable
;
603 /* Now check for needing recovery. */
604 if (flags
& NTDB_LOCK_NOCHECK
)
607 berr
= ntdb_needs_recovery(ntdb
);
608 if (likely(berr
== false))
611 ntdb_allrecord_unlock(ntdb
, ltype
);
613 return NTDB_OFF_TO_ERR(berr
);
614 ecode
= ntdb_lock_and_recover(ntdb
);
615 if (ecode
!= NTDB_SUCCESS
) {
621 enum NTDB_ERROR
ntdb_lock_open(struct ntdb_context
*ntdb
,
622 int ltype
, enum ntdb_lock_flags flags
)
624 return ntdb_nest_lock(ntdb
, NTDB_OPEN_LOCK
, ltype
, flags
);
627 void ntdb_unlock_open(struct ntdb_context
*ntdb
, int ltype
)
629 ntdb_nest_unlock(ntdb
, NTDB_OPEN_LOCK
, ltype
);
632 bool ntdb_has_open_lock(struct ntdb_context
*ntdb
)
634 return !(ntdb
->flags
& NTDB_NOLOCK
)
635 && find_nestlock(ntdb
, NTDB_OPEN_LOCK
, ntdb
) != NULL
;
638 enum NTDB_ERROR
ntdb_lock_expand(struct ntdb_context
*ntdb
, int ltype
)
640 /* Lock doesn't protect data, so don't check (we recurse if we do!) */
641 return ntdb_nest_lock(ntdb
, NTDB_EXPANSION_LOCK
, ltype
,
642 NTDB_LOCK_WAIT
| NTDB_LOCK_NOCHECK
);
645 void ntdb_unlock_expand(struct ntdb_context
*ntdb
, int ltype
)
647 ntdb_nest_unlock(ntdb
, NTDB_EXPANSION_LOCK
, ltype
);
650 /* unlock entire db */
651 void ntdb_allrecord_unlock(struct ntdb_context
*ntdb
, int ltype
)
653 if (ntdb
->flags
& NTDB_NOLOCK
)
656 if (ntdb
->file
->allrecord_lock
.count
== 0) {
657 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
658 "ntdb_allrecord_unlock: not locked!");
662 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
663 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
664 "ntdb_allrecord_unlock: not locked by us!");
668 /* Upgradable locks are marked as write locks. */
669 if (ntdb
->file
->allrecord_lock
.ltype
!= ltype
670 && (!ntdb
->file
->allrecord_lock
.off
|| ltype
!= F_RDLCK
)) {
671 ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
672 "ntdb_allrecord_unlock: have %s lock",
673 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
678 if (ntdb
->file
->allrecord_lock
.count
> 1) {
679 ntdb
->file
->allrecord_lock
.count
--;
683 ntdb
->file
->allrecord_lock
.count
= 0;
684 ntdb
->file
->allrecord_lock
.ltype
= 0;
686 ntdb_brunlock(ntdb
, ltype
, NTDB_HASH_LOCK_START
, 0);
689 bool ntdb_has_expansion_lock(struct ntdb_context
*ntdb
)
691 return find_nestlock(ntdb
, NTDB_EXPANSION_LOCK
, ntdb
) != NULL
;
694 bool ntdb_has_hash_locks(struct ntdb_context
*ntdb
)
698 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
699 if (ntdb
->file
->lockrecs
[i
].off
>= NTDB_HASH_LOCK_START
700 && ntdb
->file
->lockrecs
[i
].off
< (NTDB_HASH_LOCK_START
701 + (1 << ntdb
->hash_bits
)))
707 static bool ntdb_has_free_lock(struct ntdb_context
*ntdb
)
711 if (ntdb
->flags
& NTDB_NOLOCK
)
714 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
715 if (ntdb
->file
->lockrecs
[i
].off
716 > NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
))
722 enum NTDB_ERROR
ntdb_lock_hash(struct ntdb_context
*ntdb
,
726 unsigned l
= NTDB_HASH_LOCK_START
+ h
;
728 assert(h
< (1 << ntdb
->hash_bits
));
730 /* a allrecord lock allows us to avoid per chain locks */
731 if (ntdb
->file
->allrecord_lock
.count
) {
732 if (!check_lock_pid(ntdb
, "ntdb_lock_hashes", true))
733 return NTDB_ERR_LOCK
;
735 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
)
736 return owner_conflict(ntdb
, "ntdb_lock_hashes");
737 if (ltype
== ntdb
->file
->allrecord_lock
.ltype
738 || ltype
== F_RDLCK
) {
742 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
744 " already have %s allrecordlock",
745 ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
749 if (ntdb_has_free_lock(ntdb
)) {
750 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
751 "ntdb_lock_hashes: already have free lock");
754 if (ntdb_has_expansion_lock(ntdb
)) {
755 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
757 " already have expansion lock");
760 return ntdb_nest_lock(ntdb
, l
, ltype
, NTDB_LOCK_WAIT
);
763 enum NTDB_ERROR
ntdb_unlock_hash(struct ntdb_context
*ntdb
,
764 unsigned int h
, int ltype
)
766 unsigned l
= NTDB_HASH_LOCK_START
+ (h
& ((1 << ntdb
->hash_bits
)-1));
768 if (ntdb
->flags
& NTDB_NOLOCK
)
771 /* a allrecord lock allows us to avoid per chain locks */
772 if (ntdb
->file
->allrecord_lock
.count
) {
773 if (ntdb
->file
->allrecord_lock
.ltype
== F_RDLCK
774 && ltype
== F_WRLCK
) {
775 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
776 "ntdb_unlock_hashes RO allrecord!");
778 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
779 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_USE_ERROR
,
780 "ntdb_unlock_hashes:"
781 " not locked by us!");
786 return ntdb_nest_unlock(ntdb
, l
, ltype
);
789 /* Hash locks use NTDB_HASH_LOCK_START + <number of hash entries>..
790 * Then we begin; bucket offsets are sizeof(ntdb_len_t) apart, so we divide.
791 * The result is that on 32 bit systems we don't use lock values > 2^31 on
792 * files that are less than 4GB.
794 static ntdb_off_t
free_lock_off(const struct ntdb_context
*ntdb
,
797 return NTDB_HASH_LOCK_START
+ (1 << ntdb
->hash_bits
)
798 + b_off
/ sizeof(ntdb_off_t
);
801 enum NTDB_ERROR
ntdb_lock_free_bucket(struct ntdb_context
*ntdb
, ntdb_off_t b_off
,
802 enum ntdb_lock_flags waitflag
)
804 assert(b_off
>= sizeof(struct ntdb_header
));
806 if (ntdb
->flags
& NTDB_NOLOCK
)
809 /* a allrecord lock allows us to avoid per chain locks */
810 if (ntdb
->file
->allrecord_lock
.count
) {
811 if (!check_lock_pid(ntdb
, "ntdb_lock_free_bucket", true))
812 return NTDB_ERR_LOCK
;
814 if (ntdb
->file
->allrecord_lock
.owner
!= ntdb
) {
815 return owner_conflict(ntdb
, "ntdb_lock_free_bucket");
818 if (ntdb
->file
->allrecord_lock
.ltype
== F_WRLCK
)
820 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
821 "ntdb_lock_free_bucket with"
822 " read-only allrecordlock!");
826 if (ntdb_has_expansion_lock(ntdb
)) {
827 return ntdb_logerr(ntdb
, NTDB_ERR_LOCK
, NTDB_LOG_ERROR
,
828 "ntdb_lock_free_bucket:"
829 " already have expansion lock");
833 return ntdb_nest_lock(ntdb
, free_lock_off(ntdb
, b_off
), F_WRLCK
,
837 void ntdb_unlock_free_bucket(struct ntdb_context
*ntdb
, ntdb_off_t b_off
)
839 if (ntdb
->file
->allrecord_lock
.count
)
842 ntdb_nest_unlock(ntdb
, free_lock_off(ntdb
, b_off
), F_WRLCK
);
845 _PUBLIC_
enum NTDB_ERROR
ntdb_lockall(struct ntdb_context
*ntdb
)
847 return ntdb_allrecord_lock(ntdb
, F_WRLCK
, NTDB_LOCK_WAIT
, false);
850 _PUBLIC_
void ntdb_unlockall(struct ntdb_context
*ntdb
)
852 ntdb_allrecord_unlock(ntdb
, F_WRLCK
);
855 _PUBLIC_
enum NTDB_ERROR
ntdb_lockall_read(struct ntdb_context
*ntdb
)
857 return ntdb_allrecord_lock(ntdb
, F_RDLCK
, NTDB_LOCK_WAIT
, false);
860 _PUBLIC_
void ntdb_unlockall_read(struct ntdb_context
*ntdb
)
862 ntdb_allrecord_unlock(ntdb
, F_RDLCK
);
865 void ntdb_lock_cleanup(struct ntdb_context
*ntdb
)
869 /* We don't want to warn: they're allowed to close ntdb after fork. */
870 if (!check_lock_pid(ntdb
, "ntdb_close", false))
873 while (ntdb
->file
->allrecord_lock
.count
874 && ntdb
->file
->allrecord_lock
.owner
== ntdb
) {
875 ntdb_allrecord_unlock(ntdb
, ntdb
->file
->allrecord_lock
.ltype
);
878 for (i
=0; i
<ntdb
->file
->num_lockrecs
; i
++) {
879 if (ntdb
->file
->lockrecs
[i
].owner
== ntdb
) {
880 ntdb_nest_unlock(ntdb
,
881 ntdb
->file
->lockrecs
[i
].off
,
882 ntdb
->file
->lockrecs
[i
].ltype
);