• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/netatalk-3.0.5/libatalk/tdb/
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
32void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
33{
34	tdb->interrupt_sig_ptr = ptr;
35}
36
37/* a byte range locking function - return 0 on success
38   this functions locks/unlocks 1 byte at the specified offset.
39
40   On error, errno is also set so that errors are passed back properly
41   through tdb_open().
42
43   note that a len of zero means lock to end of file
44*/
45int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset,
46	       int rw_type, int lck_type, int probe, size_t len)
47{
48	struct flock fl;
49	int ret;
50
51	if (tdb->flags & TDB_NOLOCK) {
52		return 0;
53	}
54
55	if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) {
56		tdb->ecode = TDB_ERR_RDONLY;
57		return -1;
58	}
59
60	fl.l_type = rw_type;
61	fl.l_whence = SEEK_SET;
62	fl.l_start = offset;
63	fl.l_len = len;
64	fl.l_pid = 0;
65
66	do {
67		ret = fcntl(tdb->fd,lck_type,&fl);
68
69		/* Check for a sigalarm break. */
70		if (ret == -1 && errno == EINTR &&
71				tdb->interrupt_sig_ptr &&
72				*tdb->interrupt_sig_ptr) {
73			break;
74		}
75	} while (ret == -1 && errno == EINTR);
76
77	if (ret == -1) {
78		tdb->ecode = TDB_ERR_LOCK;
79		/* Generic lock error. errno set by fcntl.
80		 * EAGAIN is an expected return from non-blocking
81		 * locks. */
82		if (!probe && lck_type != F_SETLK) {
83			TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",
84				 tdb->fd, offset, rw_type, lck_type, (int)len));
85		}
86		return -1;
87	}
88	return 0;
89}
90
91
92/*
93  upgrade a read lock to a write lock. This needs to be handled in a
94  special way as some OSes (such as solaris) have too conservative
95  deadlock detection and claim a deadlock when progress can be
96  made. For those OSes we may loop for a while.
97*/
98int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
99{
100	int count = 1000;
101	while (count--) {
102		struct timeval tv;
103		if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) {
104			return 0;
105		}
106		if (errno != EDEADLK) {
107			break;
108		}
109		/* sleep for as short a time as we can - more portable than usleep() */
110		tv.tv_sec = 0;
111		tv.tv_usec = 1;
112		select(0, NULL, NULL, NULL, &tv);
113	}
114	TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset));
115	return -1;
116}
117
118
119/* lock a list in the database. list -1 is the alloc list */
120static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op)
121{
122	struct tdb_lock_type *new_lck;
123	int i;
124	bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
125
126	ltype &= ~TDB_MARK_LOCK;
127
128	/* a global lock allows us to avoid per chain locks */
129	if (tdb->global_lock.count &&
130	    (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
131		return 0;
132	}
133
134	if (tdb->global_lock.count) {
135		tdb->ecode = TDB_ERR_LOCK;
136		return -1;
137	}
138
139	if (list < -1 || list >= (int)tdb->header.hash_size) {
140		tdb->ecode = TDB_ERR_LOCK;
141		TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n",
142			   list, ltype));
143		return -1;
144	}
145	if (tdb->flags & TDB_NOLOCK)
146		return 0;
147
148	for (i=0; i<tdb->num_lockrecs; i++) {
149		if (tdb->lockrecs[i].list == list) {
150			if (tdb->lockrecs[i].count == 0) {
151				/*
152				 * Can't happen, see tdb_unlock(). It should
153				 * be an assert.
154				 */
155				TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
156					 "lck->count == 0 for list %d", list));
157			}
158			/*
159			 * Just increment the in-memory struct, posix locks
160			 * don't stack.
161			 */
162			tdb->lockrecs[i].count++;
163			return 0;
164		}
165	}
166
167	new_lck = (struct tdb_lock_type *)realloc(
168		tdb->lockrecs,
169		sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
170	if (new_lck == NULL) {
171		errno = ENOMEM;
172		return -1;
173	}
174	tdb->lockrecs = new_lck;
175
176	/* Since fcntl locks don't nest, we do a lock for the first one,
177	   and simply bump the count for future ones */
178	if (!mark_lock &&
179	    tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op,
180				     0, 1)) {
181		return -1;
182	}
183
184	tdb->num_locks++;
185
186	tdb->lockrecs[tdb->num_lockrecs].list = list;
187	tdb->lockrecs[tdb->num_lockrecs].count = 1;
188	tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
189	tdb->num_lockrecs += 1;
190
191	return 0;
192}
193
194/* lock a list in the database. list -1 is the alloc list */
195int tdb_lock(struct tdb_context *tdb, int list, int ltype)
196{
197	int ret;
198	ret = _tdb_lock(tdb, list, ltype, F_SETLKW);
199	if (ret) {
200		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
201			 "ltype=%d (%s)\n",  list, ltype, strerror(errno)));
202	}
203	return ret;
204}
205
206/* lock a list in the database. list -1 is the alloc list. non-blocking lock */
207int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype)
208{
209	return _tdb_lock(tdb, list, ltype, F_SETLK);
210}
211
212
213/* unlock the database: returns void because it's too late for errors. */
214	/* changed to return int it may be interesting to know there
215	   has been an error  --simo */
216int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
217{
218	int ret = -1;
219	int i;
220	struct tdb_lock_type *lck = NULL;
221	bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
222
223	ltype &= ~TDB_MARK_LOCK;
224
225	/* a global lock allows us to avoid per chain locks */
226	if (tdb->global_lock.count &&
227	    (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
228		return 0;
229	}
230
231	if (tdb->global_lock.count) {
232		tdb->ecode = TDB_ERR_LOCK;
233		return -1;
234	}
235
236	if (tdb->flags & TDB_NOLOCK)
237		return 0;
238
239	/* Sanity checks */
240	if (list < -1 || list >= (int)tdb->header.hash_size) {
241		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
242		return ret;
243	}
244
245	for (i=0; i<tdb->num_lockrecs; i++) {
246		if (tdb->lockrecs[i].list == list) {
247			lck = &tdb->lockrecs[i];
248			break;
249		}
250	}
251
252	if ((lck == NULL) || (lck->count == 0)) {
253		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
254		return -1;
255	}
256
257	if (lck->count > 1) {
258		lck->count--;
259		return 0;
260	}
261
262	/*
263	 * This lock has count==1 left, so we need to unlock it in the
264	 * kernel. We don't bother with decrementing the in-memory array
265	 * element, we're about to overwrite it with the last array element
266	 * anyway.
267	 */
268
269	if (mark_lock) {
270		ret = 0;
271	} else {
272		ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
273					       F_SETLKW, 0, 1);
274	}
275	tdb->num_locks--;
276
277	/*
278	 * Shrink the array by overwriting the element just unlocked with the
279	 * last array element.
280	 */
281
282	if (tdb->num_lockrecs > 1) {
283		*lck = tdb->lockrecs[tdb->num_lockrecs-1];
284	}
285	tdb->num_lockrecs -= 1;
286
287	/*
288	 * We don't bother with realloc when the array shrinks, but if we have
289	 * a completely idle tdb we should get rid of the locked array.
290	 */
291
292	if (tdb->num_lockrecs == 0) {
293		SAFE_FREE(tdb->lockrecs);
294	}
295
296	if (ret)
297		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
298	return ret;
299}
300
301/*
302  get the transaction lock
303 */
304int tdb_transaction_lock(struct tdb_context *tdb, int ltype)
305{
306	if (tdb->global_lock.count) {
307		return 0;
308	}
309	if (tdb->transaction_lock_count > 0) {
310		tdb->transaction_lock_count++;
311		return 0;
312	}
313
314	if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype,
315				     F_SETLKW, 0, 1) == -1) {
316		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n"));
317		tdb->ecode = TDB_ERR_LOCK;
318		return -1;
319	}
320	tdb->transaction_lock_count++;
321	return 0;
322}
323
324/*
325  release the transaction lock
326 */
327int tdb_transaction_unlock(struct tdb_context *tdb)
328{
329	int ret;
330	if (tdb->global_lock.count) {
331		return 0;
332	}
333	if (tdb->transaction_lock_count > 1) {
334		tdb->transaction_lock_count--;
335		return 0;
336	}
337	ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
338	if (ret == 0) {
339		tdb->transaction_lock_count = 0;
340	}
341	return ret;
342}
343
344
345
346
347/* lock/unlock entire database */
348static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op)
349{
350	bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
351
352	ltype &= ~TDB_MARK_LOCK;
353
354	/* There are no locks on read-only dbs */
355	if (tdb->read_only || tdb->traverse_read) {
356		tdb->ecode = TDB_ERR_LOCK;
357		return -1;
358	}
359
360	if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) {
361		tdb->global_lock.count++;
362		return 0;
363	}
364
365	if (tdb->global_lock.count) {
366		/* a global lock of a different type exists */
367		tdb->ecode = TDB_ERR_LOCK;
368		return -1;
369	}
370
371	if (tdb->num_locks != 0) {
372		/* can't combine global and chain locks */
373		tdb->ecode = TDB_ERR_LOCK;
374		return -1;
375	}
376
377	if (!mark_lock &&
378	    tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op,
379				     0, 4*tdb->header.hash_size)) {
380		if (op == F_SETLKW) {
381			TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
382		}
383		return -1;
384	}
385
386	tdb->global_lock.count = 1;
387	tdb->global_lock.ltype = ltype;
388
389	return 0;
390}
391
392
393
394/* unlock entire db */
395static int _tdb_unlockall(struct tdb_context *tdb, int ltype)
396{
397	bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK);
398
399	ltype &= ~TDB_MARK_LOCK;
400
401	/* There are no locks on read-only dbs */
402	if (tdb->read_only || tdb->traverse_read) {
403		tdb->ecode = TDB_ERR_LOCK;
404		return -1;
405	}
406
407	if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) {
408		tdb->ecode = TDB_ERR_LOCK;
409		return -1;
410	}
411
412	if (tdb->global_lock.count > 1) {
413		tdb->global_lock.count--;
414		return 0;
415	}
416
417	if (!mark_lock &&
418	    tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW,
419				     0, 4*tdb->header.hash_size)) {
420		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno)));
421		return -1;
422	}
423
424	tdb->global_lock.count = 0;
425	tdb->global_lock.ltype = 0;
426
427	return 0;
428}
429
430/* lock entire database with write lock */
431int tdb_lockall(struct tdb_context *tdb)
432{
433	tdb_trace(tdb, "tdb_lockall");
434	return _tdb_lockall(tdb, F_WRLCK, F_SETLKW);
435}
436
437/* lock entire database with write lock - mark only */
438int tdb_lockall_mark(struct tdb_context *tdb)
439{
440	tdb_trace(tdb, "tdb_lockall_mark");
441	return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW);
442}
443
444/* unlock entire database with write lock - unmark only */
445int tdb_lockall_unmark(struct tdb_context *tdb)
446{
447	tdb_trace(tdb, "tdb_lockall_unmark");
448	return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK);
449}
450
451/* lock entire database with write lock - nonblocking varient */
452int tdb_lockall_nonblock(struct tdb_context *tdb)
453{
454	int ret = _tdb_lockall(tdb, F_WRLCK, F_SETLK);
455	tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret);
456	return ret;
457}
458
459/* unlock entire database with write lock */
460int tdb_unlockall(struct tdb_context *tdb)
461{
462	tdb_trace(tdb, "tdb_unlockall");
463	return _tdb_unlockall(tdb, F_WRLCK);
464}
465
466/* lock entire database with read lock */
467int tdb_lockall_read(struct tdb_context *tdb)
468{
469	tdb_trace(tdb, "tdb_lockall_read");
470	return _tdb_lockall(tdb, F_RDLCK, F_SETLKW);
471}
472
473/* lock entire database with read lock - nonblock varient */
474int tdb_lockall_read_nonblock(struct tdb_context *tdb)
475{
476	int ret = _tdb_lockall(tdb, F_RDLCK, F_SETLK);
477	tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret);
478	return ret;
479}
480
481/* unlock entire database with read lock */
482int tdb_unlockall_read(struct tdb_context *tdb)
483{
484	tdb_trace(tdb, "tdb_unlockall_read");
485	return _tdb_unlockall(tdb, F_RDLCK);
486}
487
488/* lock/unlock one hash chain. This is meant to be used to reduce
489   contention - it cannot guarantee how many records will be locked */
490int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
491{
492	int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
493	tdb_trace_1rec(tdb, "tdb_chainlock", key);
494	return ret;
495}
496
497/* lock/unlock one hash chain, non-blocking. This is meant to be used
498   to reduce contention - it cannot guarantee how many records will be
499   locked */
500int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)
501{
502	int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
503	tdb_trace_1rec_ret(tdb, "tdb_chainlock_nonblock", key, ret);
504	return ret;
505}
506
507/* mark a chain as locked without actually locking it. Warning! use with great caution! */
508int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key)
509{
510	int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
511	tdb_trace_1rec(tdb, "tdb_chainlock_mark", key);
512	return ret;
513}
514
515/* unmark a chain as locked without actually locking it. Warning! use with great caution! */
516int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)
517{
518	tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key);
519	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK);
520}
521
522int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
523{
524	tdb_trace_1rec(tdb, "tdb_chainunlock", key);
525	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
526}
527
528int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
529{
530	int ret;
531	ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
532	tdb_trace_1rec(tdb, "tdb_chainlock_read", key);
533	return ret;
534}
535
536int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
537{
538	tdb_trace_1rec(tdb, "tdb_chainunlock_read", key);
539	return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
540}
541
542
543
544/* record lock stops delete underneath */
545int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
546{
547	if (tdb->global_lock.count) {
548		return 0;
549	}
550	return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0;
551}
552
553/*
554  Write locks override our own fcntl readlocks, so check it here.
555  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
556  an error to fail to get the lock here.
557*/
558int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
559{
560	struct tdb_traverse_lock *i;
561	for (i = &tdb->travlocks; i; i = i->next)
562		if (i->off == off)
563			return -1;
564	return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1);
565}
566
567/*
568  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
569  an error to fail to get the lock here.
570*/
571int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off)
572{
573	return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1);
574}
575
576/* fcntl locks don't stack: avoid unlocking someone else's */
577int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
578{
579	struct tdb_traverse_lock *i;
580	uint32_t count = 0;
581
582	if (tdb->global_lock.count) {
583		return 0;
584	}
585
586	if (off == 0)
587		return 0;
588	for (i = &tdb->travlocks; i; i = i->next)
589		if (i->off == off)
590			count++;
591	return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0);
592}
593