source: branches/samba-3.3.x/source/lib/tdb/common/lock.c

Last change on this file was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 19.9 KB
Line 
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_MARK_LOCK 0x80000000
31
32
33#if 1 // for debugging...
34
35#if 0
36#define F_GETLK 7 /* get record locking information */
37#define F_SETLK 8 /* set record locking information */
38#define F_SETLKW 9 /* F_SETLK; wait if blocked */
39#define F_RDLCK 1 /* shared or read lock */
40#define F_UNLCK 2 /* unlock */
41#define F_WRLCK 3 /* exclusive or write lock */
42#endif
43
44static char* lock_type( int lck)
45{
46 static char buffer[16];
47 switch(lck) {
48 case F_GETLK: return "F_GETLK";
49 case F_SETLK: return "F_SETLK";
50 case F_SETLKW: return "F_SETLKW";
51 default:
52 sprintf( buffer, "unknown %d", lck);
53 }
54 return buffer;
55}
56static char* read_type( int rw)
57{
58 static char buffer[16];
59 switch(rw) {
60 case F_RDLCK: return "F_RDLCK";
61 case F_UNLCK: return "F_UNLCK";
62 case F_WRLCK: return "F_WRLCK";
63 default:
64 sprintf( buffer, "unknown %d", rw);
65 }
66 return buffer;
67}
68#endif
69
70#ifdef __OS2__
71
72static int _mutex_brlock(struct tdb_context *tdb, tdb_off_t offset,
73 int rw_type, int lck_type, int probe, size_t len)
74{
75 HMTX hSem;
76 ULONG ulTimeout;
77 APIRET rc;
78
79 switch( offset) {
80 case GLOBAL_LOCK:
81 hSem = tdb->hGlobalLock;
82 break;
83 case ACTIVE_LOCK:
84 hSem = tdb->hActiveLock;
85 break;
86 case TRANSACTION_LOCK:
87 hSem = tdb->hTransactionLock;
88 break;
89 default:
90 printf( "_mutex_brlock unknown offset %d\n", offset);
91 exit(1);
92 }
93 if (hSem == 0) {
94 printf( "_mutex_brlock unknown sem handle offset %d\n", offset);
95 exit(1);
96 }
97
98 TDB_LOG((tdb, TDB_DEBUG_TRACE,"_mutex_brlock handle %d, offset %d\n", hSem, offset));
99
100 if (lck_type == F_SETLKW)
101 ulTimeout = SEM_INDEFINITE_WAIT;
102 else
103 ulTimeout = SEM_IMMEDIATE_RETURN;
104
105 switch (rw_type) {
106 case F_UNLCK:
107 rc = DosReleaseMutexSem( hSem);
108 break;
109 case F_RDLCK:
110 case F_WRLCK:
111 rc = DosRequestMutexSem( hSem, ulTimeout);
112 break;
113 default:
114 printf( "_mutex_brlock unknown rw_type request %d\n", rw_type);
115 exit(1);
116 break;
117 }
118
119 if (rc == NO_ERROR
120 || rc == ERROR_SEM_OWNER_DIED
121 || rc == ERROR_NOT_OWNER)
122 return 0;
123
124 errno = EINVAL;
125#if 1
126 TDB_LOG(( tdb, TDB_DEBUG_ERROR, "_mutex_brlock pid %X, failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d, rc=%d\n",
127 getpid(), tdb->fd, offset, rw_type, lck_type, (int)len, rc));
128#endif
129 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
130}
131
132#endif
133
134void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
135{
136 tdb->interrupt_sig_ptr = ptr;
137}
138
139/* a byte range locking function - return 0 on success
140 this functions locks/unlocks 1 byte at the specified offset.
141
142 On error, errno is also set so that errors are passed back properly
143 through tdb_open().
144
145 note that a len of zero means lock to end of file
146*/
147int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset,
148 int rw_type, int lck_type, int probe, size_t len)
149{
150#ifdef __OS2__
151 APIRET rc;
152 ULONG fAccess = 0;
153 int fLock = 0;
154 ULONG ulTimeout;
155 off_t cbFile;
156 off_t offStart;
157 off_t cbRange;
158
159#if 1
160 TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d\n",
161 getpid(), tdb->fd, lock_type(lck_type), read_type(rw_type), offset, len));
162#endif
163
164 switch( offset) {
165 case GLOBAL_LOCK:
166 case ACTIVE_LOCK:
167 case TRANSACTION_LOCK:
168 return _mutex_brlock( tdb, offset, rw_type, lck_type, probe, len);
169 }
170
171 if (tdb->flags & TDB_NOLOCK) {
172 return 0;
173 }
174
175 if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) {
176 tdb->ecode = TDB_ERR_RDONLY;
177 return -1;
178 }
179
180 /* flags and order */
181 fAccess = 0; /* exclusive */
182 switch (rw_type)
183 {
184 case F_UNLCK:
185 fLock = 0;
186 break;
187 case F_RDLCK:
188 fAccess = 1; /* read-only */
189 case F_WRLCK:
190 fLock = 1;
191 break;
192 default:
193 break;
194 }
195
196 if (lck_type == F_SETLKW)
197 ulTimeout = SEM_INDEFINITE_WAIT;
198 else
199 ulTimeout = SEM_IMMEDIATE_RETURN;
200
201 FILELOCK aflock[2];
202 bzero(&aflock[(fLock + 1) & 1], sizeof(aflock[0]));
203 aflock[fLock].lOffset = offset;
204 aflock[fLock].lRange = len ? len : LONG_MAX;
205 rc = DosSetFileLocks(tdb->fd, &aflock[0], &aflock[1], SEM_IMMEDIATE_RETURN, fAccess);
206#if 0
207 if (rc != NO_ERROR) {
208 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, rc=%d FAILED\n",
209 getpid(), tdb->fd, rc));
210 }
211#endif
212 if (rc != NO_ERROR && lck_type == F_SETLKW) {
213#if 0
214 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, rc=%d RETRY WAIT\n",
215 getpid(), tdb->fd, rc));
216#endif
217 int count = 20;
218 do {
219 rc = DosSetFileLocks(tdb->fd, &aflock[0], &aflock[1], 100, fAccess);
220#if 0
221 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, rc=%d RETRY WAIT(%d)\n",
222 getpid(), tdb->fd, rc,count));
223#endif
224 count--;
225 } while( count>0 && rc !=NO_ERROR);
226
227 }
228 if (rc != NO_ERROR) {
229 errno = EINVAL;
230 /* Generic lock error. errno set by fcntl.
231 * EAGAIN is an expected return from non-blocking
232 * locks. */
233 if (!probe && lck_type != F_SETLK) {
234 /* Ensure error code is set for log fun to examine. */
235 tdb->ecode = TDB_ERR_LOCK;
236 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
237 tdb->fd, offset, rw_type, lck_type, (int)len));
238 }
239#if 1
240 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
241 getpid(), tdb->fd, offset, rw_type, lck_type, (int)len));
242#endif
243 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
244 }
245#if 1
246 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d DONE\n",
247 getpid(), tdb->fd, lock_type(lck_type), read_type(rw_type), offset, len));
248#endif
249
250#else
251
252 struct flock fl;
253 int ret;
254
255 if (tdb->flags & TDB_NOLOCK) {
256 return 0;
257 }
258
259 if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) {
260 tdb->ecode = TDB_ERR_RDONLY;
261 return -1;
262 }
263
264 fl.l_type = rw_type;
265 fl.l_whence = SEEK_SET;
266 fl.l_start = offset;
267 fl.l_len = len;
268 fl.l_pid = 0;
269
270 do {
271 ret = fcntl(tdb->fd,lck_type,&fl);
272
273 /* Check for a sigalarm break. */
274 if (ret == -1 && errno == EINTR &&
275 tdb->interrupt_sig_ptr &&
276 *tdb->interrupt_sig_ptr) {
277 break;
278 }
279 } while (ret == -1 && errno == EINTR);
280
281 if (ret == -1) {
282 /* Generic lock error. errno set by fcntl.
283 * EAGAIN is an expected return from non-blocking
284 * locks. */
285 if (!probe && lck_type != F_SETLK) {
286 /* Ensure error code is set for log fun to examine. */
287 tdb->ecode = TDB_ERR_LOCK;
288 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
289 tdb->fd, offset, rw_type, lck_type, (int)len));
290 }
291 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
292 }
293
294#endif
295 return 0;
296}
297
298
299/*
300 upgrade a read lock to a write lock. This needs to be handled in a
301 special way as some OSes (such as solaris) have too conservative
302 deadlock detection and claim a deadlock when progress can be
303 made. For those OSes we may loop for a while.
304*/
305int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
306{
307 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock_upgrade pid %X, fd %d, offset %d, len %d\n",
308 getpid(), tdb->fd, offset, len));
309 int count = 1000;
310 while (count--) {
311 struct timeval tv;
312#ifdef __OS2__
313 // YD we cannot upgrade without an unlock first...
314 tdb_brlock(tdb, offset, F_UNLCK, F_SETLKW, 1, len);
315#endif
316 if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) {
317 return 0;
318 }
319 if (errno != EDEADLK) {
320 break;
321 }
322
323 /* sleep for as short a time as we can - more portable than usleep() */
324 tv.tv_sec = 0;
325 tv.tv_usec = 1;
326 select(0, NULL, NULL, NULL, &tv);
327 }
328
329 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset));
330 return -1;
331}
332
333
334/* lock a list in the database. list -1 is the alloc list */
335static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op)
336{
337 struct tdb_lock_type *new_lck;
338 int i;
339 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
340
341 ltype &= ~TDB_MARK_LOCK;
342
343 /* a global lock allows us to avoid per chain locks */
344 if (tdb->global_lock.count &&
345 (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
346 return 0;
347 }
348
349 if (tdb->global_lock.count) {
350 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
351 }
352
353 if (list < -1 || list >= (int)tdb->header.hash_size) {
354 TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n",
355 list, ltype));
356 return -1;
357 }
358 if (tdb->flags & TDB_NOLOCK)
359 return 0;
360
361 for (i=0; i<tdb->num_lockrecs; i++) {
362 if (tdb->lockrecs[i].list == list) {
363 if (tdb->lockrecs[i].count == 0) {
364 /*
365 * Can't happen, see tdb_unlock(). It should
366 * be an assert.
367 */
368 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
369 "lck->count == 0 for list %d", list));
370 }
371 /*
372 * Just increment the in-memory struct, posix locks
373 * don't stack.
374 */
375 tdb->lockrecs[i].count++;
376 return 0;
377 }
378 }
379
380 new_lck = (struct tdb_lock_type *)realloc(
381 tdb->lockrecs,
382 sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
383 if (new_lck == NULL) {
384 errno = ENOMEM;
385 return -1;
386 }
387 tdb->lockrecs = new_lck;
388
389 /* Since fcntl locks don't nest, we do a lock for the first one,
390 and simply bump the count for future ones */
391 if (!mark_lock &&
392 tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op,
393 0, 1)) {
394 return -1;
395 }
396
397 tdb->num_locks++;
398
399 tdb->lockrecs[tdb->num_lockrecs].list = list;
400 tdb->lockrecs[tdb->num_lockrecs].count = 1;
401 tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
402 tdb->num_lockrecs += 1;
403
404 return 0;
405}
406
407/* lock a list in the database. list -1 is the alloc list */
408int tdb_lock(struct tdb_context *tdb, int list, int ltype)
409{
410 int ret;
411 ret = _tdb_lock(tdb, list, ltype, F_SETLKW);
412 if (ret) {
413 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
414 "ltype=%d (%s)\n", list, ltype, strerror(errno)));
415 }
416 return ret;
417}
418
419/* lock a list in the database. list -1 is the alloc list. non-blocking lock */
420int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
421{
422 return _tdb_lock(tdb, list, ltype, F_SETLK);
423}
424
425
426/* unlock the database: returns void because it's too late for errors. */
427 /* changed to return int it may be interesting to know there
428 has been an error --simo */
429int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
430{
431 int ret = -1;
432 int i;
433 struct tdb_lock_type *lck = NULL;
434 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
435
436 ltype &= ~TDB_MARK_LOCK;
437
438 /* a global lock allows us to avoid per chain locks */
439 if (tdb->global_lock.count &&
440 (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
441 return 0;
442 }
443
444 if (tdb->global_lock.count) {
445 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
446 }
447
448 if (tdb->flags & TDB_NOLOCK)
449 return 0;
450
451 /* Sanity checks */
452 if (list < -1 || list >= (int)tdb->header.hash_size) {
453 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
454 return ret;
455 }
456
457 for (i=0; i<tdb->num_lockrecs; i++) {
458 if (tdb->lockrecs[i].list == list) {
459 lck = &tdb->lockrecs[i];
460 break;
461 }
462 }
463
464 if ((lck == NULL) || (lck->count == 0)) {
465 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
466 return -1;
467 }
468
469 if (lck->count > 1) {
470 lck->count--;
471 return 0;
472 }
473
474 /*
475 * This lock has count==1 left, so we need to unlock it in the
476 * kernel. We don't bother with decrementing the in-memory array
477 * element, we're about to overwrite it with the last array element
478 * anyway.
479 */
480
481 if (mark_lock) {
482 ret = 0;
483 } else {
484 ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
485 F_SETLKW, 0, 1);
486 }
487 tdb->num_locks--;
488
489 /*
490 * Shrink the array by overwriting the element just unlocked with the
491 * last array element.
492 */
493
494 if (tdb->num_lockrecs > 1) {
495 *lck = tdb->lockrecs[tdb->num_lockrecs-1];
496 }
497 tdb->num_lockrecs -= 1;
498
499 /*
500 * We don't bother with realloc when the array shrinks, but if we have
501 * a completely idle tdb we should get rid of the locked array.
502 */
503
504 if (tdb->num_lockrecs == 0) {
505 SAFE_FREE(tdb->lockrecs);
506 }
507
508 if (ret)
509 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
510 return ret;
511}
512
513/*
514 get the transaction lock
515 */
516int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
517{
518 if (tdb->have_transaction_lock || tdb->global_lock.count) {
519 return 0;
520 }
521 if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype,
522 F_SETLKW, 0, 1) == -1) {
523 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n"));
524 tdb->ecode = TDB_ERR_LOCK;
525 return -1;
526 }
527 tdb->have_transaction_lock = 1;
528 return 0;
529}
530
531/*
532 release the transaction lock
533 */
534int tdb_transaction_unlock(struct tdb_context *tdb)
535{
536 int ret;
537 if (!tdb->have_transaction_lock) {
538 return 0;
539 }
540 ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
541 if (ret == 0) {
542 tdb->have_transaction_lock = 0;
543 }
544 return ret;
545}
546
547
548
549
550/* lock/unlock entire database */
551static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op)
552{
553 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
554
555 ltype &= ~TDB_MARK_LOCK;
556
557 /* There are no locks on read-only dbs */
558 if (tdb->read_only || tdb->traverse_read)
559 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
560
561 if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) {
562 tdb->global_lock.count++;
563 return 0;
564 }
565
566 if (tdb->global_lock.count) {
567 /* a global lock of a different type exists */
568 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
569 }
570
571 if (tdb->num_locks != 0) {
572 /* can't combine global and chain locks */
573 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
574 }
575
576 if (!mark_lock &&
577 tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op,
578 0, 4*tdb->header.hash_size)) {
579 if (op == F_SETLKW) {
580 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
581 }
582 return -1;
583 }
584
585 tdb->global_lock.count = 1;
586 tdb->global_lock.ltype = ltype;
587
588 return 0;
589}
590
591
592
593/* unlock entire db */
594static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
595{
596 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
597
598 ltype &= ~TDB_MARK_LOCK;
599
600 /* There are no locks on read-only dbs */
601 if (tdb->read_only || tdb->traverse_read) {
602 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
603 }
604
605 if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) {
606 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
607 }
608
609 if (tdb->global_lock.count > 1) {
610 tdb->global_lock.count--;
611 return 0;
612 }
613
614 if (!mark_lock &&
615 tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW,
616 0, 4*tdb->header.hash_size)) {
617 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
618 return -1;
619 }
620
621 tdb->global_lock.count = 0;
622 tdb->global_lock.ltype = 0;
623
624 return 0;
625}
626
627/* lock entire database with write lock */
628int tdb_lockall(struct tdb_context *tdb)
629{
630 return _tdb_lockall(tdb, F_WRLCK, F_SETLKW);
631}
632
633/* lock entire database with write lock - mark only */
634int tdb_lockall_mark(struct tdb_context *tdb)
635{
636 return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW);
637}
638
639/* unlock entire database with write lock - unmark only */
640int tdb_lockall_unmark(struct tdb_context *tdb)
641{
642 return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK);
643}
644
645/* lock entire database with write lock - nonblocking varient */
646int tdb_lockall_nonblock(struct tdb_context *tdb)
647{
648 return _tdb_lockall(tdb, F_WRLCK, F_SETLK);
649}
650
651/* unlock entire database with write lock */
652int tdb_unlockall(struct tdb_context *tdb)
653{
654 return _tdb_unlockall(tdb, F_WRLCK);
655}
656
657/* lock entire database with read lock */
658int tdb_lockall_read(struct tdb_context *tdb)
659{
660 return _tdb_lockall(tdb, F_RDLCK, F_SETLKW);
661}
662
663/* lock entire database with read lock - nonblock varient */
664int tdb_lockall_read_nonblock(struct tdb_context *tdb)
665{
666 return _tdb_lockall(tdb, F_RDLCK, F_SETLK);
667}
668
669/* unlock entire database with read lock */
670int tdb_unlockall_read(struct tdb_context *tdb)
671{
672 return _tdb_unlockall(tdb, F_RDLCK);
673}
674
675/* lock/unlock one hash chain. This is meant to be used to reduce
676 contention - it cannot guarantee how many records will be locked */
677int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
678{
679 return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
680}
681
682/* lock/unlock one hash chain, non-blocking. This is meant to be used
683 to reduce contention - it cannot guarantee how many records will be
684 locked */
685int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
686{
687 return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
688}
689
690/* mark a chain as locked without actually locking it. Warning! use with great caution! */
691int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
692{
693 return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
694}
695
696/* unmark a chain as locked without actually locking it. Warning! use with great caution! */
697int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
698{
699 return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
700}
701
702int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
703{
704 return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
705}
706
707int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
708{
709 return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
710}
711
712int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
713{
714 return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
715}
716
717
718
719/* record lock stops delete underneath */
720int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
721{
722 if (tdb->global_lock.count) {
723 return 0;
724 }
725 return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0;
726}
727
728/*
729 Write locks override our own fcntl readlocks, so check it here.
730 Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
731 an error to fail to get the lock here.
732*/
733int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
734{
735 struct tdb_traverse_lock *i;
736 for (i = &tdb->travlocks; i; i = i->next)
737 if (i->off == off)
738 return -1;
739 return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1);
740}
741
742/*
743 Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
744 an error to fail to get the lock here.
745*/
746int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off)
747{
748 return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1);
749}
750
751/* fcntl locks don't stack: avoid unlocking someone else's */
752int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
753{
754 struct tdb_traverse_lock *i;
755 uint32_t count = 0;
756
757 if (tdb->global_lock.count) {
758 return 0;
759 }
760
761 if (off == 0)
762 return 0;
763 for (i = &tdb->travlocks; i; i = i->next)
764 if (i->off == off)
765 count++;
766 return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0);
767}
768
Note: See TracBrowser for help on using the repository browser.