1 /*
2  * Old binary tree database backend.
3  *
4  * Copyright 2007 Andrew Wood, distributed under the Artistic License.
5  */
6 
7 #include "config.h"
8 #include "database.h"
9 #include "log.h"
10 
11 #ifdef USING_OBTREE
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef HAVE_FCNTL
22 #include <fcntl.h>
23 #endif
24 
25 #define BT_RECORD_SIZE sizeof(struct bt_record)
26 #define BT_TOKEN_MAX   36
27 
28 typedef unsigned long bt_ul;
29 
30 struct bt_record {			 /* single binary tree record */
31 	bt_ul lower;			 /* offset of "lower" token */
32 	bt_ul higher;			 /* offset of "higher" token */
33 	unsigned char token[BT_TOKEN_MAX];	/* RATS: ignore - 0-term. token */
34 	long data[2];			 /* token data (i.e. the counts) */
35 };
36 
37 typedef struct bt_record *bt_record_t;
38 
39 struct qdbint_s {			 /* database state */
40 	int fd;				 /* file descriptor of database file */
41 	bt_ul size;			 /* total size of database */
42 #ifdef HAVE_FCNTL
43 	int lockcount;			 /* number of times lock asked for */
44 	int locktype;			 /* type of lock to use (read or write) */
45 #endif
46 	int gotheadoffs;		 /* flag, set once head offset read */
47 	bt_ul head_offset;		 /* offset of head record of tree */
48 };
49 
50 static char *bt_lasterror = "";
51 
52 
53 #ifdef HAVE_FCNTL
54 /*
55  * Obtain / release a read or write lock on the database. Returns nonzero on
56  * error, and blocks until a lock can be obtained.
57  */
dbbt_lock(qdbint_t db,int lock_type)58 static int dbbt_lock(qdbint_t db, int lock_type)
59 {
60 	int ret;
61 	ret = qdb_int__lock(db->fd, lock_type, &(db->lockcount));
62 	if (ret != 0) {
63 		bt_lasterror = strerror(errno);
64 		return 1;
65 	}
66 	return 0;
67 }
68 #endif				/* HAVE_FCNTL */
69 
70 
71 /*
72  * Analogue of fread().
73  */
dbbt_chunkread(void * ptr,int size,int nmemb,int fd)74 static int dbbt_chunkread(void *ptr, int size, int nmemb, int fd)
75 {
76 	int numread, togo, got;
77 
78 	for (numread = 0; nmemb > 0; nmemb--, numread++) {
79 		for (togo = size; togo > 0;) {
80 			got = read(fd, ptr, togo);	/* RATS: ignore (OK) */
81 			if (got <= 0)
82 				return numread;
83 			togo -= got;
84 			ptr = (void *) (((char *) ptr) + got);
85 		}
86 	}
87 
88 	return numread;
89 }
90 
91 
92 /*
93  * Analogue of fwrite().
94  */
dbbt_chunkwrite(void * ptr,int size,int nmemb,int fd)95 static int dbbt_chunkwrite(void *ptr, int size, int nmemb, int fd)
96 {
97 	int numwritten, togo, written;
98 
99 	for (numwritten = 0; nmemb > 0; nmemb--, numwritten++) {
100 		for (togo = size; togo > 0;) {
101 			written = write(fd, ptr, togo);
102 			if (written <= 0)
103 				return numwritten;
104 			togo -= written;
105 			ptr = (void *) (((char *) ptr) + written);
106 		}
107 	}
108 
109 	return numwritten;
110 }
111 
112 
113 /*
114  * Read a record from the database at the given offset into the given record
115  * structure, returning nonzero on failure.
116  */
dbbt_read_record(qdbint_t db,bt_ul offset,bt_record_t record)117 static int dbbt_read_record(qdbint_t db, bt_ul offset, bt_record_t record)
118 {
119 	int got;
120 
121 	if (lseek(db->fd, offset, SEEK_SET) == (off_t) - 1) {
122 		bt_lasterror = strerror(errno);
123 		return 1;
124 	}
125 
126 	got = dbbt_chunkread(record, BT_RECORD_SIZE, 1, db->fd);
127 	if (got < 1) {
128 		bt_lasterror = strerror(errno);
129 		return 1;
130 	}
131 
132 	record->token[BT_TOKEN_MAX - 1] = 0;
133 
134 	return 0;
135 }
136 
137 
138 /*
139  * Write a record to the database at the given offset, returning nonzero on
140  * failure.
141  */
dbbt_write_record(qdbint_t db,bt_ul offset,bt_record_t record)142 static int dbbt_write_record(qdbint_t db, bt_ul offset, bt_record_t record)
143 {
144 	if (lseek(db->fd, offset, SEEK_SET) == (off_t) - 1) {
145 		bt_lasterror = strerror(errno);
146 		return 1;
147 	}
148 
149 	if (dbbt_chunkwrite(record, BT_RECORD_SIZE, 1, db->fd) < 1) {
150 		bt_lasterror = strerror(errno);
151 		return 1;
152 	}
153 
154 	return 0;
155 }
156 
157 
158 /*
159  * Find the given token in the database and fill in the given record
160  * structure if found, also filling in the offset of the record (or 0 if not
161  * found) and the offset of the parent record (0 if none).
162  *
163  * Returns -1 if the token looked for was "lower" than its parent or +1 if
164  * "higher", or 0 if there was no parent record (i.e. this is the first
165  * record).
166  */
dbbt_find_token(qdbint_t db,qdb_datum key,bt_record_t record,bt_ul * offset,bt_ul * parent)167 static int dbbt_find_token(qdbint_t db, qdb_datum key, bt_record_t record,
168 			   bt_ul * offset, bt_ul * parent)
169 {
170 	int hilow = 0;
171 	int x;
172 
173 	*offset = 0;
174 	*parent = 0;
175 
176 	if (db == NULL)
177 		return 0;
178 
179 	if (db->size < 2 * sizeof(long))
180 		return 0;
181 
182 	if (!db->gotheadoffs) {
183 		lseek(db->fd, 0, SEEK_SET);
184 		dbbt_chunkread(offset, sizeof(*offset), 1, db->fd);
185 		db->head_offset = *offset;
186 		db->gotheadoffs = 1;
187 	} else {
188 		*offset = db->head_offset;
189 	}
190 
191 	while (*offset > 0) {
192 		int len;
193 
194 		if (dbbt_read_record(db, *offset, record)) {
195 			*offset = 0;
196 			break;
197 		}
198 
199 		x = strncmp((char *) (record->token), (char *) (key.data),
200 			    key.size);
201 		len = strlen((char *) (record->token));
202 		if (len < key.size) {
203 			x = -1;
204 		} else if (len > key.size) {
205 			x = 1;
206 		}
207 
208 		if (x == 0) {
209 			return hilow;
210 		} else if (x < 0) {
211 			*parent = *offset;
212 			hilow = -1;
213 			*offset = record->lower;
214 		} else {
215 			*parent = *offset;
216 			hilow = 1;
217 			*offset = record->higher;
218 		}
219 	}
220 
221 	return hilow;
222 }
223 
224 
225 /*
226  * Return nonzero if the given file is of this database type.
227  */
qdb_obtree_identify(const char * file)228 int qdb_obtree_identify(const char *file)
229 {
230 	if (file == NULL)
231 		return 0;
232 	if (strncasecmp(file, "obtree:", 7) == 0)
233 		return 1;
234 	return 0;
235 }
236 
237 
238 /*
239  * Open the given database in the given way (new database, read-only, or
240  * read-write); return a qdbint_t or NULL on error.
241  */
qdb_obtree_open(const char * file,qdb_open_t method)242 qdbint_t qdb_obtree_open(const char *file, qdb_open_t method)
243 {
244 	qdbint_t db;
245 	int fd = -1;
246 #ifdef HAVE_FCNTL
247 	int locktype = F_RDLCK;
248 #endif
249 	int forced_type = 0;
250 
251 	if (strncasecmp(file, "obtree:", 7) == 0) {
252 		file += 7;
253 		forced_type = 1;
254 	}
255 
256 	switch (method) {
257 	case QDB_NEW:
258 		fd = open(file,		    /* RATS: ignore (no race) */
259 			  O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
260 		if (fd < 0)
261 			bt_lasterror = strerror(errno);
262 #ifdef HAVE_FCNTL
263 		locktype = F_WRLCK;
264 #endif
265 		break;
266 	case QDB_READONLY:
267 		fd = open(file,		    /* RATS: ignore (no race) */
268 			  O_RDONLY);
269 		if (fd < 0)
270 			bt_lasterror = strerror(errno);
271 		break;
272 	case QDB_READWRITE:
273 		fd = open(file,		    /* RATS: ignore (no race) */
274 			  O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
275 		if (fd < 0)
276 			bt_lasterror = strerror(errno);
277 #ifdef HAVE_FCNTL
278 		locktype = F_WRLCK;
279 #endif
280 		break;
281 	default:
282 		break;
283 	}
284 
285 	if (fd < 0)
286 		return NULL;
287 
288 	db = calloc(1, sizeof(*db));
289 	if (db == NULL) {
290 		bt_lasterror = strerror(errno);
291 		close(fd);
292 		return NULL;
293 	}
294 
295 	db->size = lseek(fd, 0, SEEK_END);
296 	db->fd = fd;
297 
298 #ifdef HAVE_FCNTL
299 	db->locktype = locktype;
300 
301 	if (dbbt_lock(db, locktype)) {
302 		close(fd);
303 		free(db);
304 		return NULL;
305 	}
306 #endif
307 
308 	/*
309 	 * Complain at the user to stop using this backend if it wasn't
310 	 * specifically chosen.
311 	 */
312 	if (!forced_type) {
313 		log_add(0, "%s",
314 			_("WARNING: Using deprecated obtree backend!"));
315 		log_add(0, "%s",
316 			_("WARNING: Dump, delete, and restore your"));
317 		log_add(0, "%s",
318 			_("WARNING: databases to upgrade them to the"));
319 		log_add(0, "%s",
320 			_("WARNING: new format and stop this warning."));
321 	} else {
322 		log_add(1, "%s",
323 			_("warning: obtree backend is deprecated"));
324 	}
325 
326 	/*
327 	 * If the database has zero size and we're writing to it, assume
328 	 * it's new and don't complain.
329 	 */
330 	if ((db->size == 0) && (method != QDB_READONLY))
331 		return db;
332 
333 	/*
334 	 * We now do some simple checks to make sure that the file is a
335 	 * database of the format we're expecting.
336 	 */
337 
338 	/*
339 	 * If it's shorter than 2 long ints, it's not a valid database.
340 	 */
341 	if (db->size < 2 * sizeof(long)) {
342 		bt_lasterror = _("invalid database (too small)");
343 		close(fd);
344 		free(db);
345 		return NULL;
346 	}
347 
348 	/*
349 	 * If its size, discounting the two longs at the start, isn't a
350 	 * multiple of our record size, it's not a valid database.
351 	 */
352 	if (((db->size - 2 * sizeof(long)) % BT_RECORD_SIZE) != 0) {
353 		bt_lasterror = _("invalid database (irregular size)");
354 		close(fd);
355 		free(db);
356 		return NULL;
357 	}
358 
359 	/*
360 	 * If the first long int (the "head offset") is larger than the size
361 	 * of the file, this isn't a valid database.
362 	 */
363 	{
364 		bt_ul offset = db->size + 1;
365 		lseek(fd, 0, SEEK_SET);
366 		dbbt_chunkread(&offset, sizeof(offset), 1, fd);
367 		if (offset > db->size) {
368 			bt_lasterror =
369 			    _("invalid database (bad head offset)");
370 			close(fd);
371 			free(db);
372 			return NULL;
373 		}
374 		lseek(fd, 0, SEEK_SET);
375 	}
376 
377 	return db;
378 }
379 
380 
381 /*
382  * Close the given database.
383  */
qdb_obtree_close(qdbint_t db)384 void qdb_obtree_close(qdbint_t db)
385 {
386 	if (db == NULL)
387 		return;
388 
389 #ifdef HAVE_FCNTL
390 	while (db->lockcount > 0)
391 		dbbt_lock(db, F_UNLCK);
392 #endif
393 
394 	close(db->fd);
395 
396 	free(db);
397 }
398 
399 
400 /*
401  * Fetch a value from the database. The datum returned needs its val.data
402  * free()ing after use. If val.data is NULL, no value was found for the
403  * given key.
404  */
qdb_obtree_fetch(qdbint_t db,qdb_datum key)405 qdb_datum qdb_obtree_fetch(qdbint_t db, qdb_datum key)
406 {
407 	struct bt_record record;
408 	unsigned long offset, parent;
409 	qdb_datum val;
410 
411 	val.data = NULL;
412 	val.size = 0;
413 
414 	if (db == NULL)
415 		return val;
416 
417 	if (key.size >= BT_TOKEN_MAX)
418 		key.size = BT_TOKEN_MAX - 1;
419 
420 	dbbt_find_token(db, key, &record, &offset, &parent);
421 
422 	if (offset == 0)
423 		return val;
424 
425 	val.size = 2 * sizeof(long);
426 	val.data = calloc(1, val.size);
427 	if (val.data == NULL) {
428 		val.size = 0;
429 		return val;
430 	}
431 
432 	((long *) val.data)[0] = record.data[0];
433 	((long *) val.data)[1] = record.data[1];
434 
435 	return val;
436 }
437 
438 
439 /*
440  * Return a file descriptor for the given database, or -1 on error.
441  */
qdb_obtree_fd(qdbint_t db)442 int qdb_obtree_fd(qdbint_t db)
443 {
444 	if (db == NULL)
445 		return -1;
446 	return db->fd;
447 }
448 
449 
450 /*
451  * Store the given key with the given value into the database, replacing any
452  * existing value for that key. Returns nonzero on error.
453  */
qdb_obtree_store(qdbint_t db,qdb_datum key,qdb_datum val)454 int qdb_obtree_store(qdbint_t db, qdb_datum key, qdb_datum val)
455 {
456 	struct bt_record record, head;
457 	unsigned long offset, parent, nextfree;
458 	int x;
459 
460 	if (db == NULL)
461 		return 1;
462 
463 	memset(&head, 0, BT_RECORD_SIZE);
464 	memset(&record, 0, BT_RECORD_SIZE);
465 
466 	if (key.size >= BT_TOKEN_MAX)
467 		key.size = BT_TOKEN_MAX - 1;
468 
469 	x = dbbt_find_token(db, key, &record, &offset, &parent);
470 
471 	memcpy(record.token, key.data, key.size);
472 	record.token[key.size] = 0;
473 	record.data[0] = ((long *) val.data)[0];
474 	record.data[1] = ((long *) val.data)[1];
475 
476 	qdb_int__sig_block();
477 
478 	/*
479 	 * Record exists - overwrite it.
480 	 */
481 	if (offset > 0) {
482 		if (dbbt_write_record(db, offset, &record)) {
483 			qdb_int__sig_unblock();
484 			return 1;
485 		}
486 		qdb_int__sig_unblock();
487 		return 0;
488 	}
489 
490 	record.lower = 0;
491 	record.higher = 0;
492 
493 	/*
494 	 * Database has just been created, so fill in the header and write
495 	 * this record as the first one.
496 	 */
497 	if (db->size <= sizeof(offset)) {
498 
499 		if (lseek(db->fd, 0, SEEK_SET) == (off_t) - 1) {
500 			bt_lasterror = strerror(errno);
501 			qdb_int__sig_unblock();
502 			return 1;
503 		}
504 		offset = 2 * sizeof(offset);
505 		if (dbbt_chunkwrite(&offset, sizeof(offset), 1, db->fd) <
506 		    1) {
507 			bt_lasterror = strerror(errno);
508 			qdb_int__sig_unblock();
509 			return 1;
510 		}
511 
512 		db->gotheadoffs = 0;
513 
514 		offset = 0;
515 		if (dbbt_chunkwrite(&offset, sizeof(offset), 1, db->fd) <
516 		    1) {
517 			bt_lasterror = strerror(errno);
518 			qdb_int__sig_unblock();
519 			return 1;
520 		}
521 
522 		head.lower = (2 * sizeof(offset)) + BT_RECORD_SIZE;
523 		if (dbbt_chunkwrite(&head, BT_RECORD_SIZE, 1, db->fd) < 1) {
524 			bt_lasterror = strerror(errno);
525 			qdb_int__sig_unblock();
526 			return 1;
527 		}
528 
529 		if (dbbt_chunkwrite(&record, BT_RECORD_SIZE, 1, db->fd) <
530 		    1) {
531 			bt_lasterror = strerror(errno);
532 			qdb_int__sig_unblock();
533 			return 1;
534 		}
535 
536 		db->size = lseek(db->fd, 0, SEEK_CUR);
537 		qdb_int__sig_unblock();
538 		return 0;
539 	}
540 
541 	/*
542 	 * Get offset of next free space block.
543 	 */
544 	if (lseek(db->fd, sizeof(offset), SEEK_SET) == (off_t) - 1) {
545 		bt_lasterror = strerror(errno);
546 		qdb_int__sig_unblock();
547 		return 1;
548 	}
549 	if (dbbt_chunkread(&offset, sizeof(offset), 1, db->fd) < 1) {
550 		bt_lasterror = strerror(errno);
551 		qdb_int__sig_unblock();
552 		return 1;
553 	}
554 
555 	offset &= 0x7FFFFFFF;
556 
557 	/*
558 	 * If offset is 0 or we can't read from that offset, it's a new
559 	 * block at the end of the file, otherwise we take the next free
560 	 * offset from there and store it in the core free pointer, and then
561 	 * use that free offset.
562 	 */
563 	if (lseek(db->fd, offset, SEEK_SET) == (off_t) - 1) {
564 		bt_lasterror = strerror(errno);
565 		qdb_int__sig_unblock();
566 		return 1;
567 	}
568 	if ((offset == 0)
569 	    || (dbbt_chunkread(&nextfree, sizeof(nextfree), 1, db->fd) < 1)
570 	    ) {
571 		offset = lseek(db->fd, 0, SEEK_END);
572 		nextfree = 0x80000000;
573 	}
574 	if (lseek(db->fd, sizeof(offset), SEEK_SET) == (off_t) - 1) {
575 		bt_lasterror = strerror(errno);
576 		qdb_int__sig_unblock();
577 		return 1;
578 	}
579 	if (dbbt_chunkwrite(&nextfree, sizeof(nextfree), 1, db->fd) < 0) {
580 		bt_lasterror = strerror(errno);
581 		qdb_int__sig_unblock();
582 		return 1;
583 	}
584 
585 	if (dbbt_write_record(db, offset, &record)) {
586 		qdb_int__sig_unblock();
587 		return 1;
588 	}
589 
590 	/*
591 	 * Now attach the new record to its parent, if applicable.
592 	 */
593 	if (parent > 0) {
594 		if (dbbt_read_record(db, parent, &record)) {
595 			qdb_int__sig_unblock();
596 			return 1;
597 		}
598 		if (x < 0) {
599 			record.lower = offset;
600 		} else {
601 			record.higher = offset;
602 		}
603 		if (dbbt_write_record(db, parent, &record)) {
604 			qdb_int__sig_unblock();
605 			return 1;
606 		}
607 	}
608 
609 	qdb_int__sig_unblock();
610 
611 	return 0;
612 }
613 
614 
615 /*
616  * Return the "first" key in the database, suitable for using with repeated
617  * calls to qdb_nextkey() to walk through every key in the database.
618  */
qdb_obtree_firstkey(qdbint_t db)619 qdb_datum qdb_obtree_firstkey(qdbint_t db)
620 {
621 	struct bt_record record;
622 	qdb_datum key;
623 
624 	key.data = NULL;
625 	key.size = 0;
626 
627 	if (lseek(db->fd, 2 * sizeof(unsigned long), SEEK_SET) ==
628 	    (off_t) - 1)
629 		return key;
630 
631 	if (dbbt_chunkread(&record, BT_RECORD_SIZE, 1, db->fd) < 1) {
632 		return key;
633 	}
634 
635 	record.token[BT_TOKEN_MAX - 1] = 0;
636 
637 	key.data = (unsigned char *) strdup((char *) (record.token));
638 	key.size = strlen((char *) (record.token));
639 
640 	return key;
641 }
642 
643 
644 /*
645  * Return the "next" key in the database, or key.data=NULL when all keys
646  * have been returned.
647  */
qdb_obtree_nextkey(qdbint_t db,qdb_datum key)648 qdb_datum qdb_obtree_nextkey(qdbint_t db, qdb_datum key)
649 {
650 	struct bt_record record;
651 	unsigned long offset, parent;
652 	qdb_datum newkey;
653 
654 	newkey.data = NULL;
655 	newkey.size = 0;
656 
657 	if (key.data == NULL) {
658 		return newkey;
659 	}
660 
661 	dbbt_find_token(db, key, &record, &offset, &parent);
662 
663 	if (offset < 1) {
664 		return newkey;
665 	}
666 
667 	if (lseek(db->fd, offset + BT_RECORD_SIZE, SEEK_SET) ==
668 	    (off_t) - 1) {
669 		return newkey;
670 	}
671 
672 	do {
673 		if (dbbt_chunkread(&record, BT_RECORD_SIZE, 1, db->fd) < 1) {
674 			return newkey;
675 		}
676 	} while (record.lower & 0x80000000);
677 
678 	record.token[BT_TOKEN_MAX - 1] = 0;
679 
680 	newkey.data = (unsigned char *) strdup((char *) (record.token));
681 	newkey.size = strlen((char *) (record.token));
682 
683 	return newkey;
684 }
685 
686 
687 /*
688  * Reposition the given record in the binary tree, by finding an existing
689  * record to link it to. Returns nonzero on error.
690  */
qdb_obtree_delete__reposition(qdbint_t db,unsigned long offset,char * token)691 static int qdb_obtree_delete__reposition(qdbint_t db, unsigned long offset,
692 					 char *token)
693 {
694 	struct bt_record record;
695 	unsigned long offs, parent;
696 	qdb_datum key;
697 	int x;
698 
699 	key.data = (unsigned char *) token;
700 	key.size = strlen(token);
701 
702 	x = dbbt_find_token(db, key, &record, &offs, &parent);
703 
704 	if (parent < 1)
705 		return 1;
706 
707 	if (dbbt_read_record(db, parent, &record))
708 		return 1;
709 
710 	if (x < 0) {
711 		record.lower = offset;
712 	} else {
713 		record.higher = offset;
714 	}
715 
716 	if (dbbt_write_record(db, parent, &record))
717 		return 1;
718 
719 	return 0;
720 }
721 
722 
723 /*
724  * Delete the given key from the database. Returns nonzero on error.
725  */
qdb_obtree_delete(qdbint_t db,qdb_datum key)726 int qdb_obtree_delete(qdbint_t db, qdb_datum key)
727 {
728 	struct bt_record record, recparent, reclower, rechigher;
729 	unsigned long offset, parent, nextfree;
730 
731 	int x;
732 
733 	if (db == NULL)
734 		return 1;
735 
736 	if (key.size < 1)
737 		return 1;
738 
739 	x = dbbt_find_token(db, key, &record, &offset, &parent);
740 	if (offset < 1)
741 		return 1;
742 
743 	/*
744 	 * Get a copy of the lower and higher records, if any.
745 	 */
746 	if (record.lower > 0) {
747 		if (dbbt_read_record(db, record.lower, &reclower))
748 			return 1;
749 	}
750 
751 	if (record.higher > 0) {
752 		if (dbbt_read_record(db, record.higher, &rechigher))
753 			return 1;
754 	}
755 
756 	qdb_int__sig_block();
757 
758 	/*
759 	 * Remove the link to this record from its parent.
760 	 */
761 	if (parent > 0) {
762 		if (dbbt_read_record(db, parent, &recparent)) {
763 			qdb_int__sig_unblock();
764 			return 1;
765 		}
766 		if (x < 0) {
767 			recparent.lower = 0;
768 		} else {
769 			recparent.higher = 0;
770 		}
771 		if (dbbt_write_record(db, parent, &recparent)) {
772 			qdb_int__sig_unblock();
773 			return 1;
774 		}
775 	}
776 
777 
778 	/*
779 	 * Re-attach any children of this record to the database.
780 	 */
781 	if (record.lower > 0)
782 		qdb_obtree_delete__reposition(db, record.lower,
783 					      (char *) (reclower.token));
784 	if (record.higher > 0)
785 		qdb_obtree_delete__reposition(db, record.higher,
786 					      (char *) (rechigher.token));
787 
788 	/*
789 	 * Now we add this record's offset to the head of the "free records"
790 	 * linked list.
791 	 */
792 	if (lseek(db->fd, sizeof(offset), SEEK_SET) == (off_t) - 1) {
793 		bt_lasterror = strerror(errno);
794 		qdb_int__sig_unblock();
795 		return 1;
796 	}
797 
798 	nextfree = 0x80000000;
799 	if (dbbt_chunkread(&nextfree, sizeof(nextfree), 1, db->fd) < 0) {
800 		bt_lasterror = strerror(errno);
801 		qdb_int__sig_unblock();
802 		return 1;
803 	}
804 
805 	if (lseek(db->fd, sizeof(offset), SEEK_SET) == (off_t) - 1) {
806 		bt_lasterror = strerror(errno);
807 		qdb_int__sig_unblock();
808 		return 1;
809 	}
810 
811 	offset |= 0x80000000;
812 	if (dbbt_chunkwrite(&offset, sizeof(offset), 1, db->fd) < 0) {
813 		bt_lasterror = strerror(errno);
814 		qdb_int__sig_unblock();
815 		return 1;
816 	}
817 
818 	offset &= 0x7FFFFFFF;
819 
820 	if (lseek(db->fd, offset, SEEK_SET) == (off_t) - 1) {
821 		bt_lasterror = strerror(errno);
822 		qdb_int__sig_unblock();
823 		return 1;
824 	}
825 
826 	if (dbbt_chunkwrite(&nextfree, sizeof(nextfree), 1, db->fd) < 1) {
827 		bt_lasterror = strerror(errno);
828 		qdb_int__sig_unblock();
829 		return 1;
830 	}
831 
832 	qdb_int__sig_unblock();
833 
834 	return 0;
835 }
836 
837 
838 /*
839  * Temporarily release the lock on the database.
840  */
qdb_obtree_unlock(qdbint_t db)841 void qdb_obtree_unlock(qdbint_t db)
842 {
843 #ifdef HAVE_FCNTL
844 	dbbt_lock(db, F_UNLCK);
845 #endif
846 }
847 
848 
849 /*
850  * Reassert the lock on the database.
851  */
qdb_obtree_relock(qdbint_t db)852 void qdb_obtree_relock(qdbint_t db)
853 {
854 #ifdef HAVE_FCNTL
855 	dbbt_lock(db, db->locktype);
856 #endif
857 	db->gotheadoffs = 0;
858 }
859 
860 
861 /*
862  * Return a string describing the last database error to occur.
863  */
qdb_obtree_error(void)864 char *qdb_obtree_error(void)
865 {
866 	return bt_lasterror;
867 }
868 
869 #endif				/* USING_OBTREE */
870 
871 /* EOF */
872