krb5 commit: Refactor btree recursive traversal code
Tom Yu
tlyu at mit.edu
Tue Aug 16 21:46:16 EDT 2016
https://github.com/krb5/krb5/commit/590af488351316b6f4ddb70ce67a2730b4fcc40d
commit 590af488351316b6f4ddb70ce67a2730b4fcc40d
Author: Tom Yu <tlyu at mit.edu>
Date: Mon Aug 8 09:15:42 2016 -0400
Refactor btree recursive traversal code
Previous releases had a nonstandard entry point (bt_rseq) into libdb2
to perform recursive traversal of a btree database that might be
corrupt so that an operator could attempt data recovery. This entry
point became inaccessible to user commands after krb5-1.5 due to
integration of the DAL.
Refactor the recursive traversal code into the existing btree
sequential traversal code, accepting new movement flags R_RNEXT
(recursively advance to next item) and R_RPREV.
Add commands to the libdb2 btree test program bttest to exercise this
functionality. Fix up the existing "rlist" command of bttest to use
the updated interface.
ticket: 8476 (new)
subject: Restore recursive dump functionality
src/plugins/kdb/db2/libdb2/btree/bt_seq.c | 489 ++++----------------
src/plugins/kdb/db2/libdb2/btree/extern.h | 9 -
src/plugins/kdb/db2/libdb2/include/db.hin | 19 +-
src/plugins/kdb/db2/libdb2/libdb.exports | 1 -
src/plugins/kdb/db2/libdb2/test/btree.tests/main.c | 56 ++-
5 files changed, 171 insertions(+), 403 deletions(-)
diff --git a/src/plugins/kdb/db2/libdb2/btree/bt_seq.c b/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
index b39d89e..2c8c2de 100644
--- a/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
+++ b/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2002 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2002, 2016 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Export of this software from the United States of America may
@@ -77,6 +77,9 @@ static int __bt_first __P((BTREE *, const DBT *, EPG *, int *));
static int __bt_seqadv __P((BTREE *, EPG *, int));
static int __bt_seqset __P((BTREE *, EPG *, DBT *, int));
+static int bt_rseq_next(BTREE *, EPG *);
+static int bt_rseq_prev(BTREE *, EPG *);
+
/*
* Sequential scan support.
*
@@ -124,6 +127,8 @@ __bt_seq(dbp, key, data, flags)
switch (flags) {
case R_NEXT:
case R_PREV:
+ case R_RNEXT:
+ case R_RPREV:
if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
status = __bt_seqadv(t, &e, flags);
break;
@@ -202,6 +207,8 @@ __bt_seqset(t, ep, key, flags)
return (__bt_first(t, key, ep, &exact));
case R_FIRST: /* First record. */
case R_NEXT:
+ case R_RNEXT:
+ BT_CLR(t);
/* Walk down the left-hand side of the tree. */
for (pg = P_ROOT;;) {
if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
@@ -216,6 +223,7 @@ __bt_seqset(t, ep, key, flags)
if (h->flags & (P_BLEAF | P_RLEAF))
break;
pg = GETBINTERNAL(h, 0)->pgno;
+ BT_PUSH(t, h->pgno, 0);
mpool_put(t->bt_mp, h, 0);
}
ep->page = h;
@@ -223,6 +231,8 @@ __bt_seqset(t, ep, key, flags)
break;
case R_LAST: /* Last record. */
case R_PREV:
+ case R_RPREV:
+ BT_CLR(t);
/* Walk down the right-hand side of the tree. */
for (pg = P_ROOT;;) {
if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
@@ -237,6 +247,7 @@ __bt_seqset(t, ep, key, flags)
if (h->flags & (P_BLEAF | P_RLEAF))
break;
pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
+ BT_PUSH(t, h->pgno, NEXTINDEX(h) - 1);
mpool_put(t->bt_mp, h, 0);
}
@@ -330,6 +341,7 @@ __bt_seqadv(t, ep, flags)
*/
switch (flags) {
case R_NEXT: /* Next record. */
+ case R_RNEXT:
/*
* The cursor was deleted in duplicate records, and moved
* forward to a record that has yet to be returned. Clear
@@ -339,6 +351,11 @@ __bt_seqadv(t, ep, flags)
goto usecurrent;
idx = c->pg.index;
if (++idx == NEXTINDEX(h)) {
+ if (flags == R_RNEXT) {
+ ep->page = h;
+ ep->index = idx;
+ return (bt_rseq_next(t, ep));
+ }
pg = h->nextpg;
mpool_put(t->bt_mp, h, 0);
if (pg == P_INVALID)
@@ -349,6 +366,7 @@ __bt_seqadv(t, ep, flags)
}
break;
case R_PREV: /* Previous record. */
+ case R_RPREV:
/*
* The cursor was deleted in duplicate records, and moved
* backward to a record that has yet to be returned. Clear
@@ -362,6 +380,11 @@ usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE);
}
idx = c->pg.index;
if (idx == 0) {
+ if (flags == R_RPREV) {
+ ep->page = h;
+ ep->index = idx;
+ return (bt_rseq_prev(t, ep));
+ }
pg = h->prevpg;
mpool_put(t->bt_mp, h, 0);
if (pg == P_INVALID)
@@ -380,6 +403,84 @@ usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE);
}
/*
+ * Get the first item on the next page, but by going up and down the tree.
+ */
+static int
+bt_rseq_next(BTREE *t, EPG *ep)
+{
+ PAGE *h;
+ indx_t idx;
+ EPGNO *up;
+ db_pgno_t pg;
+
+ h = ep->page;
+ idx = ep->index;
+ do {
+ /* Move up the tree. */
+ up = BT_POP(t);
+ mpool_put(t->bt_mp, h, 0);
+ /* Did we hit the right edge of the root? */
+ if (up == NULL)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, up->pgno, 0)) == NULL)
+ return (RET_ERROR);
+ idx = up->index;
+ } while (++idx == NEXTINDEX(h));
+
+ while (!(h->flags & (P_BLEAF | P_RLEAF))) {
+ /* Move back down the tree. */
+ BT_PUSH(t, h->pgno, idx);
+ pg = GETBINTERNAL(h, idx)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ idx = 0;
+ }
+ ep->page = h;
+ ep->index = idx;
+ return (RET_SUCCESS);
+}
+
+/*
+ * Get the last item on the previous page, but by going up and down the tree.
+ */
+static int
+bt_rseq_prev(BTREE *t, EPG *ep)
+{
+ PAGE *h;
+ indx_t idx;
+ EPGNO *up;
+ db_pgno_t pg;
+
+ h = ep->page;
+ idx = ep->index;
+ do {
+ /* Move up the tree. */
+ up = BT_POP(t);
+ mpool_put(t->bt_mp, h, 0);
+ /* Did we hit the left edge of the root? */
+ if (up == NULL)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, up->pgno, 0)) == NULL)
+ return (RET_ERROR);
+ idx = up->index;
+ } while (idx == 0);
+ --idx;
+ while (!(h->flags & (P_BLEAF | P_RLEAF))) {
+ /* Move back down the tree. */
+ BT_PUSH(t, h->pgno, idx);
+ pg = GETBINTERNAL(h, idx)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ idx = NEXTINDEX(h) - 1;
+ }
+ ep->page = h;
+ ep->index = idx;
+ return (RET_SUCCESS);
+}
+
+/*
* __bt_first --
* Find the first entry.
*
@@ -513,389 +614,3 @@ __bt_setcur(t, pgno, idx)
t->bt_cursor.pg.index = idx;
F_SET(&t->bt_cursor, CURS_INIT);
}
-
-/* Recursive descent cursor. */
-typedef struct rcursor_ {
- CURSOR cursor;
- size_t ssize;
- EPGNO *stack;
- EPGNO *sp;
-} RCURSOR;
-#define RCURSOR_MINSS 64
-
-static int bt_rcinit(void **);
-static void bt_rcdestroy(void **);
-static int bt_rcpush(RCURSOR *, db_pgno_t, u_int);
-static EPGNO *bt_rcpop(RCURSOR *);
-static void bt_rcclr(RCURSOR *);
-static int bt_rcgrowstk(RCURSOR *);
-static int bt_rseqset(BTREE *, EPG *, DBT *, RCURSOR *, int);
-static int bt_rseqadv(BTREE *, EPG *, RCURSOR *, int);
-
-static int
-bt_rcinit(curs)
- void **curs;
-{
- RCURSOR *rc;
-
- rc = *curs = malloc(sizeof(RCURSOR));
- if (rc == NULL) {
- errno = ENOMEM;
- return RET_ERROR;
- }
- memset(rc, 0, sizeof(*rc));
-
- rc->ssize = RCURSOR_MINSS;
- rc->stack = malloc(rc->ssize * sizeof(EPGNO));
- if (rc->stack == NULL) {
- free(rc);
- errno = ENOMEM;
- return RET_ERROR;
- }
- bt_rcclr(rc);
- return RET_SUCCESS;
-}
-
-static void
-bt_rcdestroy(curs)
- void **curs;
-{
- RCURSOR *rc;
-
- rc = *curs;
- free(rc->stack);
- free(rc);
- *curs = NULL;
-}
-
-static int
-bt_rcpush(rc, p, i)
- RCURSOR *rc;
- db_pgno_t p;
- u_int i;
-{
- int status;
-
- rc->sp->pgno = p;
- rc->sp->index = i;
- if (++rc->sp > rc->stack + rc->ssize) {
- status = bt_rcgrowstk(rc);
- if (status != RET_SUCCESS)
- return status;
- }
- return RET_SUCCESS;
-}
-
-static EPGNO *
-bt_rcpop(rc)
- RCURSOR *rc;
-{
- return (rc->sp == rc->stack) ? NULL : --rc->sp;
-}
-
-static void
-bt_rcclr(rc)
- RCURSOR *rc;
-{
- rc->sp = rc->stack;
-}
-
-static int
-bt_rcgrowstk(rc)
- RCURSOR *rc;
-{
- size_t osize;
- EPGNO *e;
-
- osize = rc->ssize;
- rc->ssize *= 2;
- e = realloc(rc->stack, rc->ssize * sizeof(EPGNO));
- if (e == NULL) {
- rc->ssize = osize;
- errno = ENOMEM;
- return RET_ERROR;
- }
- rc->stack = e;
- return RET_SUCCESS;
-}
-
-/*
- * bt_rseq --
- * Like __bt_seq but does recursive descent tree traversal
- * instead of using the prev/next pointers.
- */
-int
-bt_rseq(dbp, key, data, curs, flags)
- const DB *dbp;
- DBT *key, *data;
- void **curs;
- u_int flags;
-{
- RCURSOR *rc;
- BTREE *t;
- EPG e;
- int status;
-
- t = dbp->internal;
-
- /* Toss any page pinned across calls. */
- if (t->bt_pinned != NULL) {
- mpool_put(t->bt_mp, t->bt_pinned, 0);
- t->bt_pinned = NULL;
- }
-
- if (curs == NULL) {
- errno = EINVAL;
- return RET_ERROR;
- }
- if (*curs == NULL) {
- status = bt_rcinit(curs);
- if (status != RET_SUCCESS)
- return RET_ERROR;
- }
- rc = *curs;
-
- /*
- * If scan unitialized as yet, or starting at a specific record, set
- * the scan to a specific key. Both bt_rseqset and bt_rseqadv pin
- * the page the cursor references if they're successful.
- */
- switch (flags) {
- case R_NEXT:
- case R_PREV:
- if (F_ISSET(&rc->cursor, CURS_INIT)) {
- status = bt_rseqadv(t, &e, rc, flags);
- break;
- }
- /* FALLTHROUGH */
- case R_FIRST:
- case R_LAST:
- case R_CURSOR:
- status = bt_rseqset(t, &e, key, rc, flags);
- break;
- default:
- errno = EINVAL;
- return (RET_ERROR);
- }
-
- if (status == RET_SUCCESS) {
- status =
- __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0);
-
- /*
- * If the user is doing concurrent access, we copied the
- * key/data, toss the page.
- */
- if (F_ISSET(t, B_DB_LOCK))
- mpool_put(t->bt_mp, e.page, 0);
- else
- t->bt_pinned = e.page;
- } else if (status == RET_SPECIAL)
- bt_rcdestroy(curs);
- return (status);
-}
-
-/*
- * bt_rseqset --
- * Set the sequential scan to a specific key.
- *
- * Parameters:
- * t: tree
- * ep: storage for returned key
- * key: key for initial scan position
- * rc: recursion cursor
- * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
- *
- * Side effects:
- * Pins the page the cursor references.
- * Updates rc's stack and cursor.
- *
- * Returns:
- * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
- */
-static int
-bt_rseqset(t, ep, key, rc, flags)
- BTREE *t;
- EPG *ep;
- DBT *key;
- RCURSOR *rc;
- int flags;
-{
- PAGE *h;
- db_pgno_t pg;
- int status;
-
- /*
- * Find the first, last or specific key in the tree and point the
- * cursor at it. The cursor may not be moved until a new key has
- * been found.
- */
- switch (flags) {
- case R_CURSOR: /* Not implemented. */
- errno = EINVAL;
- return RET_ERROR;
- case R_FIRST: /* First record. */
- case R_NEXT:
- bt_rcclr(rc);
- /* Walk down the left-hand side of the tree. */
- for (pg = P_ROOT;;) {
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
-
- /* Check for an empty tree. */
- if (NEXTINDEX(h) == 0) {
- mpool_put(t->bt_mp, h, 0);
- return (RET_SPECIAL);
- }
-
- if (h->flags & (P_BLEAF | P_RLEAF))
- break;
- pg = GETBINTERNAL(h, 0)->pgno;
- status = bt_rcpush(rc, h->pgno, 0);
- mpool_put(t->bt_mp, h, 0);
- if (status != RET_SUCCESS)
- return status;
- }
- ep->page = h;
- ep->index = 0;
- break;
- case R_LAST: /* Last record. */
- case R_PREV:
- bt_rcclr(rc);
- /* Walk down the right-hand side of the tree. */
- for (pg = P_ROOT;;) {
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
-
- /* Check for an empty tree. */
- if (NEXTINDEX(h) == 0) {
- mpool_put(t->bt_mp, h, 0);
- return (RET_SPECIAL);
- }
-
- if (h->flags & (P_BLEAF | P_RLEAF))
- break;
- pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
- status = bt_rcpush(rc, h->pgno, NEXTINDEX(h) - 1);
- mpool_put(t->bt_mp, h, 0);
- if (status != RET_SUCCESS)
- return status;
- }
- ep->page = h;
- ep->index = NEXTINDEX(h) - 1;
- break;
- }
- rc->cursor.pg.pgno = ep->page->pgno;
- rc->cursor.pg.index = ep->index;
- F_CLR(&rc->cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
- F_SET(&rc->cursor, CURS_INIT);
- return (RET_SUCCESS);
-}
-
-/*
- * bt_rseqadvance --
- * Advance the sequential scan.
- *
- * Parameters:
- * t: tree
- * ep: return page
- * rc: recursion cursor
- * flags: R_NEXT, R_PREV
- *
- * Side effects:
- * Pins the page the new key/data record is on.
- * Updates rc's stack and cursor.
- *
- * Returns:
- * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
- */
-static int
-bt_rseqadv(t, ep, rc, flags)
- BTREE *t;
- EPG *ep;
- RCURSOR *rc;
- int flags;
-{
- CURSOR *c;
- PAGE *h;
- indx_t idx = 0;
- db_pgno_t pg;
- int status;
- EPGNO *e;
-
- /*
- * There are a couple of states that we can be in. The cursor has
- * been initialized by the time we get here, but that's all we know.
- */
- c = &rc->cursor;
-
- /* Get the page referenced by the cursor. */
- if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
- return (RET_ERROR);
-
- /*
- * Find the next/previous record in the tree and point the cursor at
- * it. The cursor may not be moved until a new key has been found.
- */
- switch (flags) {
- case R_NEXT: /* Next record. */
- idx = c->pg.index;
- while (++idx == NEXTINDEX(h)) {
- /* Crawl up if we hit the right edge. */
- e = bt_rcpop(rc);
- mpool_put(t->bt_mp, h, 0);
- if (e == NULL) /* Hit the right edge of root. */
- return RET_SPECIAL;
- idx = e->index;
- pg = e->pgno;
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
- }
- while (!(h->flags & (P_BLEAF | P_RLEAF))) {
- /* Crawl down the left until we hit a leaf. */
- status = bt_rcpush(rc, h->pgno, idx);
- pg = GETBINTERNAL(h, idx)->pgno;
- mpool_put(t->bt_mp, h, 0);
- if (status != RET_SUCCESS)
- return status;
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
- idx = 0;
- }
- break;
- case R_PREV: /* Previous record. */
- idx = c->pg.index;
- while (!idx) {
- /* Crawl up if we hit the left edge. */
- e = bt_rcpop(rc);
- mpool_put(t->bt_mp, h, 0);
- if (e == NULL) /* Hit the left edge of root. */
- return RET_SPECIAL;
- idx = e->index;
- pg = e->pgno;
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
- }
- idx--;
- while (!(h->flags & (P_BLEAF | P_RLEAF))) {
- /* Crawl down the right until we hit a leaf. */
- status = bt_rcpush(rc, h->pgno, idx);
- pg = GETBINTERNAL(h, idx)->pgno;
- mpool_put(t->bt_mp, h, 0);
- if (status != RET_SUCCESS)
- return status;
- if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
- return (RET_ERROR);
- idx = NEXTINDEX(h) - 1;
- }
- break;
- }
-
- ep->page = h;
- ep->index = idx;
- c->pg.pgno = h->pgno;
- c->pg.index = idx;
- F_CLR(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
- F_SET(c, CURS_INIT);
- return (RET_SUCCESS);
-}
diff --git a/src/plugins/kdb/db2/libdb2/btree/extern.h b/src/plugins/kdb/db2/libdb2/btree/extern.h
index 3aa8841..c29b084 100644
--- a/src/plugins/kdb/db2/libdb2/btree/extern.h
+++ b/src/plugins/kdb/db2/libdb2/btree/extern.h
@@ -63,15 +63,6 @@
#define __bt_dump __kdb2_bt_dump
#define __bt_stat __kdb2_bt_stat
-#define bt_rcinit kdb2_bt_rcinit
-#define bt_rcdestroy kdb2_bt_rcdestroy
-#define bt_rcpush kdb2_bt_rcpush
-#define bt_rcpop kdb2_bt_rcpop
-#define bt_rcclr kdb2_bt_rcclr
-#define bt_rcgrowstk kdb2_bt_rcgrowstk
-#define bt_rseqset kdb2_bt_rseqset
-#define bt_rseqadv kdb2_bt_rseqadv
-
int __bt_close __P((DB *));
int __bt_cmp __P((BTREE *, const DBT *, EPG *));
int __bt_crsrdel __P((BTREE *, EPGNO *));
diff --git a/src/plugins/kdb/db2/libdb2/include/db.hin b/src/plugins/kdb/db2/libdb2/include/db.hin
index dca0b00..9ae8f8b 100644
--- a/src/plugins/kdb/db2/libdb2/include/db.hin
+++ b/src/plugins/kdb/db2/libdb2/include/db.hin
@@ -63,6 +63,23 @@ typedef struct {
#define R_SETCURSOR 10 /* put (RECNO) */
#define R_RECNOSYNC 11 /* sync (RECNO) */
+/*
+ * Recursive sequential scan.
+ *
+ * This avoids using sibling pointers, permitting (possibly partial)
+ * recovery from some kinds of btree corruption. Start a sequential
+ * scan as usual, but use R_RNEXT or R_RPREV to move forward or
+ * backward.
+ *
+ * This probably doesn't work with btrees that allow duplicate keys.
+ * Database modifications during the scan can also modify the parent
+ * page stack needed for correct functioning. Intermixing
+ * non-recursive traversal by using R_NEXT or R_PREV can also make the
+ * page stack inconsistent with the cursor and cause problems.
+ */
+#define R_RNEXT 128 /* seq (BTREE, RECNO) */
+#define R_RPREV 129 /* seq (BTREE, RECNO) */
+
typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE;
/*
@@ -166,10 +183,8 @@ typedef struct {
#endif
#define dbopen kdb2_dbopen
-#define bt_rseq kdb2_bt_rseq /* XXX kludge */
__BEGIN_DECLS
DB *dbopen __P((const char *, int, int, DBTYPE, const void *));
-int bt_rseq(const DB*, DBT *, DBT *, void **, u_int); /* XXX kludge */
__END_DECLS
#endif /* !_DB_H_ */
diff --git a/src/plugins/kdb/db2/libdb2/libdb.exports b/src/plugins/kdb/db2/libdb2/libdb.exports
index 4d34947..7828da1 100644
--- a/src/plugins/kdb/db2/libdb2/libdb.exports
+++ b/src/plugins/kdb/db2/libdb2/libdb.exports
@@ -73,7 +73,6 @@ __kdb2_rec_sync
__kdb2_rec_vmap
__kdb2_rec_vpipe
__kdb2_split_page
-kdb2_bt_rseq
kdb2_dbm_clearerr
kdb2_dbm_close
kdb2_dbm_delete
diff --git a/src/plugins/kdb/db2/libdb2/test/btree.tests/main.c b/src/plugins/kdb/db2/libdb2/test/btree.tests/main.c
index 5b9890b..33de7a2 100644
--- a/src/plugins/kdb/db2/libdb2/test/btree.tests/main.c
+++ b/src/plugins/kdb/db2/libdb2/test/btree.tests/main.c
@@ -94,6 +94,8 @@ void previous __P((DB *, char **));
void show __P((DB *, char **));
#endif
void rlist __P((DB *, char **));
+void rnext __P((DB *, char **));
+void rprev __P((DB *, char **));
void usage __P((void));
void user __P((DB *));
@@ -131,6 +133,8 @@ cmd_table commands[] = {
"p", 0, 0, previous, "previous", "move cursor back one record",
"q", 0, 0, NULL, "quit", "quit",
"rli", 1, 1, rlist, "rlist file", "list to a file (recursive)",
+ "rn", 0, 0, rnext, "rnext", "move cursor forward one record (recursive)",
+ "rp", 0, 0, rprev, "rprev", "move cursor back one record (recursive)",
#ifdef DEBUG
"sh", 1, 0, show, "show page", "dump a page",
#endif
@@ -652,17 +656,15 @@ rlist(db, argv)
DBT data, key;
FILE *fp;
int status;
- void *cookie;
- cookie = NULL;
if ((fp = fopen(argv[1], "w")) == NULL) {
(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
return;
}
- status = bt_rseq(db, &key, &data, &cookie, R_FIRST);
+ status = (*db->seq)(db, &key, &data, R_FIRST);
while (status == RET_SUCCESS) {
(void)fprintf(fp, "%.*s\n", (int)key.size, key.data);
- status = bt_rseq(db, &key, &data, &cookie, R_NEXT);
+ status = (*db->seq)(db, &key, &data, R_RNEXT);
}
(void)fclose(fp);
if (status == RET_ERROR)
@@ -773,6 +775,52 @@ previous(db, argv)
}
}
+void
+rnext(db, argv)
+ DB *db;
+ char **argv;
+{
+ DBT data, key;
+ int status;
+
+ status = (*db->seq)(db, &key, &data, R_RNEXT);
+
+ switch (status) {
+ case RET_ERROR:
+ perror("rnext/seq");
+ break;
+ case RET_SPECIAL:
+ (void)printf("no more keys\n");
+ break;
+ case RET_SUCCESS:
+ keydata(&key, &data);
+ break;
+ }
+}
+
+void
+rprev(db, argv)
+ DB *db;
+ char **argv;
+{
+ DBT data, key;
+ int status;
+
+ status = (*db->seq)(db, &key, &data, R_RPREV);
+
+ switch (status) {
+ case RET_ERROR:
+ perror("rprev/seq");
+ break;
+ case RET_SPECIAL:
+ (void)printf("no more keys\n");
+ break;
+ case RET_SUCCESS:
+ keydata(&key, &data);
+ break;
+ }
+}
+
#ifdef DEBUG
void
show(db, argv)
More information about the cvs-krb5
mailing list