diff options
Diffstat (limited to 'contrib/netbsd-tests/lib/libc/db')
-rw-r--r-- | contrib/netbsd-tests/lib/libc/db/README | 66 | ||||
-rw-r--r-- | contrib/netbsd-tests/lib/libc/db/h_db.c | 794 | ||||
-rw-r--r-- | contrib/netbsd-tests/lib/libc/db/h_lfsr.c | 179 | ||||
-rwxr-xr-x | contrib/netbsd-tests/lib/libc/db/t_db.sh | 1302 | ||||
-rw-r--r-- | contrib/netbsd-tests/lib/libc/db/t_db_hash_seq.c | 343 |
5 files changed, 2684 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/lib/libc/db/README b/contrib/netbsd-tests/lib/libc/db/README new file mode 100644 index 000000000000..68f0ec7a1c1a --- /dev/null +++ b/contrib/netbsd-tests/lib/libc/db/README @@ -0,0 +1,66 @@ +# $NetBSD: README,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ +# @(#)README 8.8 (Berkeley) 7/31/94 + +Fairly large files (the command files) are built in this directory during +the test runs, and even larger files (the database files) are created in +"/var/tmp". If the latter directory doesn't exist, set the environmental +variable TMPDIR to a directory where the files can be built. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +The script file consists of lines with an initial character which is +the command for that line, or an initial character indicating a key +or data entry for a previous command. + +Legal command characters are as follows: + +c: compare a record + + must be followed by [kK][dD]; the data value in the database + associated with the specified key is compared to the specified + data value. +e: echo a string + + writes out the rest of the line into the output file; if the + last character is not a carriage-return, a newline is appended. +f: set the flags for the next command + + no value zero's the flags +g: do a get command + + must be followed by [kK] + + writes out the retrieved data DBT. +o [r]: dump [reverse] + + dump the database out, if 'r' is set, in reverse order. +p: do a put command + + must be followed by [kK][dD] +r: do a del command + + must be followed by [kK] unless R_CURSOR flag set. +S: sync the database +s: do a seq command + + must be followed by [kK] if R_CURSOR flag set. + + writes out the retrieved data DBT. + +Legal key/data characters are as follows: + +D [file]: data file + + set the current data value to the contents of the file +d [data]: + + set the current key value to the contents of the line. +K [file]: key file + + set the current key value to the contents of the file +k [data]: + + set the current key value to the contents of the line. + +Blank lines, lines with leading white space, and lines with leading +hash marks (#) are ignored. + +Options to dbtest are as follows: + + -d: Set the DB_LOCK flag. + -f: Use the file argument as the database file. + -i: Use the rest of the argument to set elements in the info + structure. If the type is btree, then "-i cachesize=10240" + will set BTREEINFO.cachesize to 10240. + -o: The rest of the argument is the output file instead of + using stdout. + -s: Don't delete the database file before opening it, i.e. + use the database file from a previous run. + +Dbtest requires two arguments, the type of access "hash", "recno" +or "btree", and the script name or "-" to indicate stdin. diff --git a/contrib/netbsd-tests/lib/libc/db/h_db.c b/contrib/netbsd-tests/lib/libc/db/h_db.c new file mode 100644 index 000000000000..dd19a034aa36 --- /dev/null +++ b/contrib/netbsd-tests/lib/libc/db/h_db.c @@ -0,0 +1,794 @@ +/* $NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; +#else +__RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <err.h> +#include <db.h> +#include "btree.h" + +enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; + +static void compare(DBT *, DBT *); +static DBTYPE dbtype(const char *); +static void dump(DB *, int, int); +static void get(DB *, DBT *); +static void getdata(DB *, DBT *, DBT *); +static void put(DB *, DBT *, DBT *); +static void rem(DB *, DBT *); +static const char *sflags(int); +static void synk(DB *); +static void *rfile(char *, size_t *); +static void seq(DB *, DBT *); +static u_int setflags(char *); +static void *setinfo(DBTYPE, char *); +#ifdef __NetBSD__ +static void unlinkpg(DB *); +#endif +static void usage(void) __attribute__((__noreturn__)); +static void *xcopy(void *, size_t); +static void chkcmd(enum S); +static void chkdata(enum S); +static void chkkey(enum S); + +#ifdef STATISTICS +extern void __bt_stat(DB *); +#endif +#ifdef __NetBSD__ +extern int __bt_relink(BTREE *, PAGE *); +#endif + +static DBTYPE type; /* Database type. */ +static void *infop; /* Iflags. */ +static size_t lineno; /* Current line in test script. */ +static u_int flags; /* Current DB flags. */ +static int ofd = STDOUT_FILENO; /* Standard output fd. */ + +static DB *XXdbp; /* Global for gdb. */ +static size_t XXlineno; /* Fast breakpoint for gdb. */ + +int +main(int argc, char *argv[]) +{ + extern int optind; + extern char *optarg; + enum S command = COMMAND, state; + DB *dbp; + DBT data, key, keydata; + size_t len; + int ch, oflags, sflag; + char *fname, *infoarg, *p, *t, buf[8 * 1024]; + bool unlink_dbfile; + + infoarg = NULL; + fname = NULL; + unlink_dbfile = false; + oflags = O_CREAT | O_RDWR; + sflag = 0; + while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1) + switch (ch) { + case 'f': + fname = optarg; + break; + case 'i': + infoarg = optarg; + break; + case 'l': + oflags |= DB_LOCK; + break; + case 'o': + if ((ofd = open(optarg, + O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + err(1, "Cannot create `%s'", optarg); + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* Set the type. */ + type = dbtype(*argv++); + + /* Open the descriptor file. */ + if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) + err(1, "Cannot reopen `%s'", *argv); + + /* Set up the db structure as necessary. */ + if (infoarg == NULL) + infop = NULL; + else + for (p = strtok(infoarg, ",\t "); p != NULL; + p = strtok(0, ",\t ")) + if (*p != '\0') + infop = setinfo(type, p); + + /* + * Open the DB. Delete any preexisting copy, you almost never + * want it around, and it often screws up tests. + */ + if (fname == NULL) { + const char *q = getenv("TMPDIR"); + if (q == NULL) + q = "/var/tmp"; + (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q); + fname = buf; + (void)unlink(buf); + unlink_dbfile = true; + } else if (!sflag) + (void)unlink(fname); + + if ((dbp = dbopen(fname, + oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) + err(1, "Cannot dbopen `%s'", fname); + XXdbp = dbp; + if (unlink_dbfile) + (void)unlink(fname); + + state = COMMAND; + for (lineno = 1; + (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { + /* Delete the newline, displaying the key/data is easier. */ + if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) + *t = '\0'; + if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) || + *p == '#') + continue; + + /* Convenient gdb break point. */ + if (XXlineno == lineno) + XXlineno = 1; + switch (*p) { + case 'c': /* compare */ + chkcmd(state); + state = KEY; + command = COMPARE; + break; + case 'e': /* echo */ + chkcmd(state); + /* Don't display the newline, if CR at EOL. */ + if (p[len - 2] == '\r') + --len; + if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 || + write(ofd, "\n", 1) != 1) + err(1, "write failed"); + break; + case 'g': /* get */ + chkcmd(state); + state = KEY; + command = GET; + break; + case 'p': /* put */ + chkcmd(state); + state = KEY; + command = PUT; + break; + case 'r': /* remove */ + chkcmd(state); + if (flags == R_CURSOR) { + rem(dbp, &key); + state = COMMAND; + } else { + state = KEY; + command = REMOVE; + } + break; + case 'S': /* sync */ + chkcmd(state); + synk(dbp); + state = COMMAND; + break; + case 's': /* seq */ + chkcmd(state); + if (flags == R_CURSOR) { + state = KEY; + command = SEQ; + } else + seq(dbp, &key); + break; + case 'f': + flags = setflags(p + 1); + break; + case 'D': /* data file */ + chkdata(state); + data.data = rfile(p + 1, &data.size); + goto ldata; + case 'd': /* data */ + chkdata(state); + data.data = xcopy(p + 1, len - 1); + data.size = len - 1; +ldata: switch (command) { + case COMPARE: + compare(&keydata, &data); + break; + case PUT: + put(dbp, &key, &data); + break; + default: + errx(1, "line %zu: command doesn't take data", + lineno); + } + if (type != DB_RECNO) + free(key.data); + free(data.data); + state = COMMAND; + break; + case 'K': /* key file */ + chkkey(state); + if (type == DB_RECNO) + errx(1, "line %zu: 'K' not available for recno", + lineno); + key.data = rfile(p + 1, &key.size); + goto lkey; + case 'k': /* key */ + chkkey(state); + if (type == DB_RECNO) { + static recno_t recno; + recno = atoi(p + 1); + key.data = &recno; + key.size = sizeof(recno); + } else { + key.data = xcopy(p + 1, len - 1); + key.size = len - 1; + } +lkey: switch (command) { + case COMPARE: + getdata(dbp, &key, &keydata); + state = DATA; + break; + case GET: + get(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + case PUT: + state = DATA; + break; + case REMOVE: + rem(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + case SEQ: + seq(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + default: + errx(1, "line %zu: command doesn't take a key", + lineno); + } + break; + case 'o': + dump(dbp, p[1] == 'r', 0); + break; +#ifdef __NetBSD__ + case 'O': + dump(dbp, p[1] == 'r', 1); + break; + case 'u': + unlinkpg(dbp); + break; +#endif + default: + errx(1, "line %zu: %s: unknown command character", + lineno, p); + } + } +#ifdef STATISTICS + /* + * -l must be used (DB_LOCK must be set) for this to be + * used, otherwise a page will be locked and it will fail. + */ + if (type == DB_BTREE && oflags & DB_LOCK) + __bt_stat(dbp); +#endif + if ((*dbp->close)(dbp)) + err(1, "db->close failed"); + (void)close(ofd); + return 0; +} + +#define NOOVERWRITE "put failed, would overwrite key\n" + +static void +compare(DBT *db1, DBT *db2) +{ + size_t len; + u_char *p1, *p2; + + if (db1->size != db2->size) + printf("compare failed: key->data len %zu != data len %zu\n", + db1->size, db2->size); + + len = MIN(db1->size, db2->size); + for (p1 = db1->data, p2 = db2->data; len--;) + if (*p1++ != *p2++) { + printf("compare failed at offset %lu\n", + (unsigned long)(p1 - (u_char *)db1->data)); + break; + } +} + +static void +get(DB *dbp, DBT *kp) +{ + DBT data; + + switch ((*dbp->get)(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err(1, "line %zu: get failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "get failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, + NOSUCHKEY); +#undef NOSUCHKEY + break; + } +} + +static void +getdata(DB *dbp, DBT *kp, DBT *dp) +{ + switch ((*dbp->get)(dbp, kp, dp, flags)) { + case 0: + return; + case -1: + err(1, "line %zu: getdata failed", lineno); + /* NOTREACHED */ + case 1: + errx(1, "line %zu: getdata failed, no such key", lineno); + /* NOTREACHED */ + } +} + +static void +put(DB *dbp, DBT *kp, DBT *dp) +{ + switch ((*dbp->put)(dbp, kp, dp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: put failed", lineno); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); + break; + } +} + +static void +rem(DB *dbp, DBT *kp) +{ + switch ((*dbp->del)(dbp, kp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: rem failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "rem failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags != R_CURSOR) + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%zu: rem of cursor failed\n", lineno); +#undef NOSUCHKEY + break; + } +} + +static void +synk(DB *dbp) +{ + switch ((*dbp->sync)(dbp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: synk failed", lineno); + /* NOTREACHED */ + } +} + +static void +seq(DB *dbp, DBT *kp) +{ + DBT data; + + switch (dbp->seq(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err(1, "line %zu: seq failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "seq failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags == R_CURSOR) + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%zu: seq (%s) failed\n", lineno, sflags(flags)); +#undef NOSUCHKEY + break; + } +} + +static void +dump(DB *dbp, int rev, int recurse) +{ + DBT key, data; + int xflags, nflags; + + if (rev) { + xflags = R_LAST; +#ifdef __NetBSD__ + nflags = recurse ? R_RPREV : R_PREV; +#else + nflags = R_PREV; +#endif + } else { + xflags = R_FIRST; +#ifdef __NetBSD__ + nflags = recurse ? R_RNEXT : R_NEXT; +#else + nflags = R_NEXT; +#endif + } + for (;; xflags = nflags) + switch (dbp->seq(dbp, &key, &data, xflags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case 1: + goto done; + case -1: + err(1, "line %zu: (dump) seq failed", lineno); + /* NOTREACHED */ + } +done: return; +} + +#ifdef __NetBSD__ +void +unlinkpg(DB *dbp) +{ + BTREE *t = dbp->internal; + PAGE *h = NULL; + pgno_t pg; + + for (pg = P_ROOT; pg < t->bt_mp->npages; + mpool_put(t->bt_mp, h, 0), pg++) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + break; + /* Look for a nonempty leaf page that has both left + * and right siblings. */ + if (h->prevpg == P_INVALID || h->nextpg == P_INVALID) + continue; + if (NEXTINDEX(h) == 0) + continue; + if ((h->flags & (P_BLEAF | P_RLEAF))) + break; + } + if (h == NULL || pg == t->bt_mp->npages) { + errx(1, "%s: no appropriate page found", __func__); + return; + } + if (__bt_relink(t, h) != 0) { + perror("unlinkpg"); + goto cleanup; + } + h->prevpg = P_INVALID; + h->nextpg = P_INVALID; +cleanup: + mpool_put(t->bt_mp, h, MPOOL_DIRTY); +} +#endif + +static u_int +setflags(char *s) +{ + char *p; + + for (; isspace((unsigned char)*s); ++s); + if (*s == '\n' || *s == '\0') + return 0; + if ((p = strchr(s, '\n')) != NULL) + *p = '\0'; + if (!strcmp(s, "R_CURSOR")) return R_CURSOR; + if (!strcmp(s, "R_FIRST")) return R_FIRST; + if (!strcmp(s, "R_IAFTER")) return R_IAFTER; + if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE; + if (!strcmp(s, "R_LAST")) return R_LAST; + if (!strcmp(s, "R_NEXT")) return R_NEXT; + if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE; + if (!strcmp(s, "R_PREV")) return R_PREV; + if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR; + + errx(1, "line %zu: %s: unknown flag", lineno, s); + /* NOTREACHED */ +} + +static const char * +sflags(int xflags) +{ + switch (xflags) { + case R_CURSOR: return "R_CURSOR"; + case R_FIRST: return "R_FIRST"; + case R_IAFTER: return "R_IAFTER"; + case R_IBEFORE: return "R_IBEFORE"; + case R_LAST: return "R_LAST"; + case R_NEXT: return "R_NEXT"; + case R_NOOVERWRITE: return "R_NOOVERWRITE"; + case R_PREV: return "R_PREV"; + case R_SETCURSOR: return "R_SETCURSOR"; + } + + return "UNKNOWN!"; +} + +static DBTYPE +dbtype(const char *s) +{ + if (!strcmp(s, "btree")) + return DB_BTREE; + if (!strcmp(s, "hash")) + return DB_HASH; + if (!strcmp(s, "recno")) + return DB_RECNO; + errx(1, "%s: unknown type (use btree, hash or recno)", s); + /* NOTREACHED */ +} + +static void * +setinfo(DBTYPE dtype, char *s) +{ + static BTREEINFO ib; + static HASHINFO ih; + static RECNOINFO rh; + char *eq; + + if ((eq = strchr(s, '=')) == NULL) + errx(1, "%s: illegal structure set statement", s); + *eq++ = '\0'; + if (!isdigit((unsigned char)*eq)) + errx(1, "%s: structure set statement must be a number", s); + + switch (dtype) { + case DB_BTREE: + if (!strcmp("flags", s)) { + ib.flags = atoi(eq); + return &ib; + } + if (!strcmp("cachesize", s)) { + ib.cachesize = atoi(eq); + return &ib; + } + if (!strcmp("maxkeypage", s)) { + ib.maxkeypage = atoi(eq); + return &ib; + } + if (!strcmp("minkeypage", s)) { + ib.minkeypage = atoi(eq); + return &ib; + } + if (!strcmp("lorder", s)) { + ib.lorder = atoi(eq); + return &ib; + } + if (!strcmp("psize", s)) { + ib.psize = atoi(eq); + return &ib; + } + break; + case DB_HASH: + if (!strcmp("bsize", s)) { + ih.bsize = atoi(eq); + return &ih; + } + if (!strcmp("ffactor", s)) { + ih.ffactor = atoi(eq); + return &ih; + } + if (!strcmp("nelem", s)) { + ih.nelem = atoi(eq); + return &ih; + } + if (!strcmp("cachesize", s)) { + ih.cachesize = atoi(eq); + return &ih; + } + if (!strcmp("lorder", s)) { + ih.lorder = atoi(eq); + return &ih; + } + break; + case DB_RECNO: + if (!strcmp("flags", s)) { + rh.flags = atoi(eq); + return &rh; + } + if (!strcmp("cachesize", s)) { + rh.cachesize = atoi(eq); + return &rh; + } + if (!strcmp("lorder", s)) { + rh.lorder = atoi(eq); + return &rh; + } + if (!strcmp("reclen", s)) { + rh.reclen = atoi(eq); + return &rh; + } + if (!strcmp("bval", s)) { + rh.bval = atoi(eq); + return &rh; + } + if (!strcmp("psize", s)) { + rh.psize = atoi(eq); + return &rh; + } + break; + } + errx(1, "%s: unknown structure value", s); + /* NOTREACHED */ +} + +static void * +rfile(char *name, size_t *lenp) +{ + struct stat sb; + void *p; + int fd; + char *np; + + for (; isspace((unsigned char)*name); ++name) + continue; + if ((np = strchr(name, '\n')) != NULL) + *np = '\0'; + if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1) + err(1, "Cannot open `%s'", name); +#ifdef NOT_PORTABLE + if (sb.st_size > (off_t)SIZE_T_MAX) { + errno = E2BIG; + err("Cannot process `%s'", name); + } +#endif + if ((p = malloc((size_t)sb.st_size)) == NULL) + err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size); + if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size) + err(1, "read failed"); + *lenp = (size_t)sb.st_size; + (void)close(fd); + return p; +} + +static void * +xcopy(void *text, size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + err(1, "Cannot allocate %zu bytes", len); + (void)memmove(p, text, len); + return p; +} + +static void +chkcmd(enum S state) +{ + if (state != COMMAND) + errx(1, "line %zu: not expecting command", lineno); +} + +static void +chkdata(enum S state) +{ + if (state != DATA) + errx(1, "line %zu: not expecting data", lineno); +} + +static void +chkkey(enum S state) +{ + if (state != KEY) + errx(1, "line %zu: not expecting a key", lineno); +} + +static void +usage(void) +{ + (void)fprintf(stderr, +#ifdef __NetBSD__ + "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] " +#else + "Usage: %s [-l] [-f file] [-i info] [-o file] " +#endif + "type script\n", getprogname()); + exit(1); +} diff --git a/contrib/netbsd-tests/lib/libc/db/h_lfsr.c b/contrib/netbsd-tests/lib/libc/db/h_lfsr.c new file mode 100644 index 000000000000..3f3d712dfd85 --- /dev/null +++ b/contrib/netbsd-tests/lib/libc/db/h_lfsr.c @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2015 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/cdefs.h> +__RCSID("$NetBSD: h_lfsr.c,v 1.1 2015/11/18 18:35:35 christos Exp $"); + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <err.h> +#include <string.h> +#include <unistd.h> +#include <db.h> + +#define MAXKEY 0xffff +#ifdef DEBUG +#define DPRINTF(...) printf(__VA_ARGS__) +#else +#define DPRINTF(...) +#endif + +static uint16_t +next(uint16_t *cur) +{ + uint16_t lsb = *cur & 1; + *cur >>= 1; + *cur ^= (-lsb) & 0xB400u; + return *cur; +} + +int +main(int argc, char *argv[]) +{ + char buf[65536]; + char kb[256]; + DBT key, val; + DB *db; + HASHINFO hi; + uint8_t c; + uint16_t len; + uint32_t pagesize = atoi(argv[1]); + + memset(&hi, 0, sizeof(hi)); + memset(buf, 'a', sizeof(buf)); + hi.bsize = pagesize; + hi.nelem = 65536; + hi.ffactor = 128; + + key.data = kb; + val.data = buf; + + db = dbopen(NULL, O_CREAT|O_TRUNC|O_RDWR, 0, DB_HASH, &hi); + if (db == NULL) + err(EXIT_FAILURE, "dbopen"); + + len = 0xaec1; + for (size_t i = 0; i < MAXKEY; i++) { + key.size = (len & 0xff) + 1; + c = len >> 8; + memset(kb, c, key.size); + val.size = (next(&len) & 0xff) + 1; + switch ((*db->put)(db, &key, &val, R_NOOVERWRITE)) { + case 0: + DPRINTF("put %zu %zu %#x\n", + key.size, val.size, c); + break; + case -1: + err(EXIT_FAILURE, "put error %zu %zu %#x", + key.size, val.size, c); + case 1: + errx(EXIT_FAILURE, "put overwrite %zu %zu %#x", + key.size, val.size, c); + default: + abort(); + } + } + + len = 0xaec1; + for (size_t i = 0; i < MAXKEY; i++) { + key.size = (len & 0xff) + 1; + c = len >> 8; + memset(kb, c, key.size); + next(&len); + switch ((*db->get)(db, &key, &val, 0)) { + case 0: + DPRINTF("get %zu %zu %#x\n", + key.size, val.size, c); + break; + case -1: + err(EXIT_FAILURE, "get %zu %zu %#x", + key.size, val.size, c); + case 1: + errx(EXIT_FAILURE, "get not found %zu %zu %#x", + key.size, val.size, c); + default: + abort(); + } + if (memcmp(key.data, kb, key.size) != 0) + errx(EXIT_FAILURE, "get badkey %zu %zu %#x", + key.size, val.size, c); + if (val.size != (len & 0xff) + 1U) + errx(EXIT_FAILURE, "get badvallen %zu %zu %#x", + key.size, val.size, c); + if (memcmp(val.data, buf, val.size) != 0) + errx(EXIT_FAILURE, "get badval %zu %zu %#x", + key.size, val.size, c); + } + + len = 0xaec1; + for (size_t i = 0; i < MAXKEY; i++) { + key.size = (len & 0xff) + 1; + c = len >> 8; + memset(kb, c, key.size); + next(&len); + switch ((*db->del)(db, &key, 0)) { + case 0: + DPRINTF("del %zu %zu %#x\n", + key.size, val.size, c); + break; + case -1: + err(EXIT_FAILURE, "del %zu %zu %#x", key.size, + val.size, c); + case 1: + errx(EXIT_FAILURE, "del not found %zu %zu %#x", + key.size, val.size, c); + default: + abort(); + } + } + + len = 0xaec1; + for (size_t i = 0; i < MAXKEY; i++) { + key.size = (len & 0xff) + 1; + c = len >> 8; + memset(kb, c, key.size); + next(&len); + switch ((*db->get)(db, &key, &val, 0)) { + case 0: + errx(EXIT_FAILURE, "get2 found %zu %zu %#x", + key.size, val.size, c); + break; + case -1: + err(EXIT_FAILURE, "get2 %zu %zu %#x", + key.size, val.size, c); + case 1: + DPRINTF("get2 %zu %zu %#x\n", + key.size, val.size, c); + break; + default: + abort(); + } + } + return 0; +} diff --git a/contrib/netbsd-tests/lib/libc/db/t_db.sh b/contrib/netbsd-tests/lib/libc/db/t_db.sh new file mode 100755 index 000000000000..93a7b1ec0bb0 --- /dev/null +++ b/contrib/netbsd-tests/lib/libc/db/t_db.sh @@ -0,0 +1,1302 @@ +# $NetBSD: t_db.sh,v 1.7 2016/09/24 20:12:33 christos Exp $ +# +# Copyright (c) 2008 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +prog_db() +{ + echo $(atf_get_srcdir)/h_db +} + +prog_lfsr() +{ + echo $(atf_get_srcdir)/h_lfsr +} + +dict() +{ + if [ -f /usr/share/dict/words ]; then + echo /usr/share/dict/words + elif [ -f /usr/dict/words ]; then + echo /usr/dict/words + else + atf_fail "no dictionary found" + fi +} + +SEVEN_SEVEN="abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg" + +atf_test_case small_btree +small_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +small_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + for i in `sed 200q $(dict)`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" btree in +} + +atf_test_case small_hash +small_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +small_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + for i in `sed 200q $(dict)`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" hash in +} + +atf_test_case small_recno +small_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +small_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + sed 200q $(dict) | + awk '{ + ++i; + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' >in + + atf_check -o file:exp "$(prog_db)" recno in +} + +atf_test_case medium_btree +medium_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +medium_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + for i in $(sed 200q $(dict)); do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" btree in +} + +atf_test_case medium_hash +medium_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +medium_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + for i in $(sed 200q $(dict)); do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" hash in +} + +atf_test_case medium_recno +medium_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." +} +medium_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' >in + + atf_check -o file:exp "$(prog_db)" recno in +} + +atf_test_case big_btree +big_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + for psize in 512 16384 65536; do + echo "checking page size: $psize" + + for i in `find /bin -type f -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done >in + + atf_check "$(prog_db)" -o out btree in + cmp -s exp out || atf_fail "test failed for page size: $psize" + done +} + +atf_test_case big_hash +big_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + for i in `find /bin -type f -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done >in + + atf_check "$(prog_db)" -o out hash in + cmp -s exp out || atf_fail "test failed" +} + +atf_test_case big_recno +big_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + find /bin -type f -print | + awk '{ + ++i; + printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i); + }' >in + + for psize in 512 16384 65536; do + echo "checking page size: $psize" + + atf_check "$(prog_db)" -o out recno in + cmp -s exp out || atf_fail "test failed for page size: $psize" + done +} + +atf_test_case random_recno +random_recno_head() +{ + atf_set "descr" "Checks recno database using random entries" +} +random_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 37; i <= 37 + 88 * 17; i += 17) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 1; i <= 15; ++i) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 19234; i <= 19234 + 61 * 27; i += 27) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit + }' >exp + + cat exp | + awk 'BEGIN { + i = 37; + incr = 17; + } + { + printf("p\nk%d\nd%s\n", i, $0); + if (i == 19234 + 61 * 27) + exit; + if (i == 37 + 88 * 17) { + i = 1; + incr = 1; + } else if (i == 15) { + i = 19234; + incr = 27; + } else + i += incr; + } + END { + for (i = 37; i <= 37 + 88 * 17; i += 17) + printf("g\nk%d\n", i); + for (i = 1; i <= 15; ++i) + printf("g\nk%d\n", i); + for (i = 19234; i <= 19234 + 61 * 27; i += 27) + printf("g\nk%d\n", i); + }' >in + + atf_check -o file:exp "$(prog_db)" recno in +} + +atf_test_case reverse_recno +reverse_recno_head() +{ + atf_set "descr" "Checks recno database using reverse order entries" +} +reverse_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk ' { + for (i = 1500; i; --i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' >exp + + cat exp | + awk 'BEGIN { + i = 1500; + } + { + printf("p\nk%d\nd%s\n", i, $0); + --i; + } + END { + for (i = 1500; i; --i) + printf("g\nk%d\n", i); + }' >in + + atf_check -o file:exp "$(prog_db)" recno in +} + +atf_test_case alternate_recno +alternate_recno_head() +{ + atf_set "descr" "Checks recno database using alternating order entries" +} +alternate_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk ' { + for (i = 1; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 2; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' >exp + + cat exp | + awk 'BEGIN { + i = 1; + even = 0; + } + { + printf("p\nk%d\nd%s\n", i, $0); + i += 2; + if (i >= 1200) { + if (even == 1) + exit; + even = 1; + i = 2; + } + } + END { + for (i = 1; i < 1200; ++i) + printf("g\nk%d\n", i); + }' >in + + atf_check "$(prog_db)" -o out recno in + + sort -o exp exp + sort -o out out + + cmp -s exp out || atf_fail "test failed" +} + +h_delete() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + type=$1 + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 120; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + }' >exp + + cat exp | + awk '{ + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_NEXT\n"); + for (i = 1; i <= 120; ++i) + printf("s\n"); + printf("fR_CURSOR\ns\nkXX\n"); + printf("r\n"); + printf("fR_NEXT\ns\n"); + printf("fR_CURSOR\ns\nk1\n"); + printf("r\n"); + printf("fR_FIRST\ns\n"); + }' >in + + # For btree, the records are ordered by the string representation + # of the key value. So sort the expected output file accordingly, + # and set the seek_last key to the last expected key value. + + if [ "$type" = "btree" ] ; then + sed -e 's/kXX/k99/' < in > tmp + mv tmp in + sort -d -k4 < exp > tmp + mv tmp exp + echo $SEVEN_SEVEN | + awk '{ + printf("%05d: input key %d: %s\n", 99, 99, $0); + printf("seq failed, no such key\n"); + printf("%05d: input key %d: %s\n", 1, 1, $0); + printf("%05d: input key %d: %s\n", 10, 10, $0); + exit; + }' >> exp + else + # For recno, records are ordered by numerical key value. No sort + # is needed, but still need to set proper seek_last key value. + sed -e 's/kXX/k120/' < in > tmp + mv tmp in + echo $SEVEN_SEVEN | + awk '{ + printf("%05d: input key %d: %s\n", 120, 120, $0); + printf("seq failed, no such key\n"); + printf("%05d: input key %d: %s\n", 1, 1, $0); + printf("%05d: input key %d: %s\n", 2, 2, $0); + exit; + }' >> exp + fi + + atf_check "$(prog_db)" -o out $type in + atf_check -o file:exp cat out +} + +atf_test_case delete_btree +delete_btree_head() +{ + atf_set "descr" "Checks removing records in btree database" +} +delete_btree_body() +{ + h_delete btree +} + +atf_test_case delete_recno +delete_recno_head() +{ + atf_set "descr" "Checks removing records in recno database" +} +delete_recno_body() +{ + h_delete recno +} + +h_repeated() +{ + local type="$1" + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo "" | + awk 'BEGIN { + for (i = 1; i <= 10; ++i) { + printf("p\nkkey1\nD/bin/sh\n"); + printf("p\nkkey2\nD/bin/csh\n"); + if (i % 8 == 0) { + printf("c\nkkey2\nD/bin/csh\n"); + printf("c\nkkey1\nD/bin/sh\n"); + printf("e\t%d of 10 (comparison)\n", i); + } else + printf("e\t%d of 10 \n", i); + printf("r\nkkey1\nr\nkkey2\n"); + } + }' >in + + $(prog_db) $type in +} + +atf_test_case repeated_btree +repeated_btree_head() +{ + atf_set "descr" \ + "Checks btree database with repeated small keys and" \ + "big data pairs. Makes sure that overflow pages are reused" +} +repeated_btree_body() +{ + h_repeated btree +} + +atf_test_case repeated_hash +repeated_hash_head() +{ + atf_set "descr" \ + "Checks hash database with repeated small keys and" \ + "big data pairs. Makes sure that overflow pages are reused" +} +repeated_hash_body() +{ + h_repeated hash +} + +atf_test_case duplicate_btree +duplicate_btree_head() +{ + atf_set "descr" "Checks btree database with duplicate keys" +} +duplicate_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 543; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + cat exp | + awk '{ + if (i++ % 2) + printf("p\nkduplicatekey\nd%s\n", $0); + else + printf("p\nkunique%dkey\nd%s\n", i, $0); + } + END { + printf("o\n"); + }' >in + + atf_check -o file:exp -x "$(prog_db) -iflags=1 btree in | sort" +} + +h_cursor_flags() +{ + local type=$1 + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 20; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + # Test that R_CURSOR doesn't succeed before cursor initialized + cat exp | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\nr\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' >in + + atf_check -o ignore -e ignore -s ne:0 "$(prog_db)" -o out $type in + atf_check -s ne:0 test -s out + + cat exp | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\np\nk1\ndsome data\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' >in + + atf_check -o ignore -e ignore -s ne:0 "$(prog_db)" -o out $type in + atf_check -s ne:0 test -s out +} + +atf_test_case cursor_flags_btree +cursor_flags_btree_head() +{ + atf_set "descr" \ + "Checks use of cursor flags without initialization in btree database" +} +cursor_flags_btree_body() +{ + h_cursor_flags btree +} + +atf_test_case cursor_flags_recno +cursor_flags_recno_head() +{ + atf_set "descr" \ + "Checks use of cursor flags without initialization in recno database" +} +cursor_flags_recno_body() +{ + h_cursor_flags recno +} + +atf_test_case reverse_order_recno +reverse_order_recno_head() +{ + atf_set "descr" "Checks reverse order inserts in recno database" +} +reverse_order_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 779; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + cat exp | + awk '{ + if (i == 0) { + i = 1; + printf("p\nk1\nd%s\n", $0); + printf("%s\n", "fR_IBEFORE"); + } else + printf("p\nk1\nd%s\n", $0); + } + END { + printf("or\n"); + }' >in + + atf_check -o file:exp "$(prog_db)" recno in +} + +atf_test_case small_page_btree +small_page_btree_head() +{ + atf_set "descr" \ + "Checks btree database with lots of keys and small page" \ + "size: takes the first 20000 entries in the dictionary," \ + "reverses them, and gives them each a small size data" \ + "entry. Uses a small page size to make sure the btree" \ + "split code gets hammered." + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +small_page_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxy + echo $mdata | + awk '{ for (i = 1; i < 20001; ++i) print $0 }' >exp + + for i in `sed 20000q $(dict) | rev`; do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" -i psize=512 btree in +} + +h_byte_orders() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + type=$1 + + sed 50q $(dict) >exp + for order in 1234 4321; do + for i in `sed 50q $(dict)`; do + echo p + echo k$i + echo d$i + echo S + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" -ilorder=$order -f byte.file $type in + + for i in `sed 50q $(dict)`; do + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog_db)" -s -ilorder=$order -f byte.file $type in + done +} + +atf_test_case byte_orders_btree +byte_orders_btree_head() +{ + atf_set "descr" "Checks btree database using differing byte orders" + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +byte_orders_btree_body() +{ + h_byte_orders btree +} + +atf_test_case byte_orders_hash +byte_orders_hash_head() +{ + atf_set "descr" "Checks hash database using differing byte orders" +} +byte_orders_hash_body() +{ + h_byte_orders hash +} + +h_bsize_ffactor() +{ + bsize=$1 + ffactor=$2 + + echo "bucketsize $bsize, fill factor $ffactor" + atf_check -o file:exp "$(prog_db)" "-ibsize=$bsize,\ +ffactor=$ffactor,nelem=25000,cachesize=65536" hash in +} + +atf_test_case bsize_ffactor +bsize_ffactor_head() +{ + atf_set "timeout" "1800" + atf_set "descr" "Checks hash database with various" \ + "bucketsizes and fill factors" + # Begin FreeBSD + atf_set "require.files" /usr/share/dict/words + # End FreeBSD +} +bsize_ffactor_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 10000; ++i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("%s\n", s); + } + exit; + + }' >exp + + sed 10000q $(dict) | + awk 'BEGIN { + ds="'$SEVEN_SEVEN'" + } + { + if (++i % 34) + s = substr(ds, 1, i % 34); + else + s = substr(ds, 1); + printf("p\nk%s\nd%s\n", $0, s); + }' >in + + sed 10000q $(dict) | + awk '{ + ++i; + printf("g\nk%s\n", $0); + }' >>in + + h_bsize_ffactor 256 11 + h_bsize_ffactor 256 14 + h_bsize_ffactor 256 21 + + h_bsize_ffactor 512 21 + h_bsize_ffactor 512 28 + h_bsize_ffactor 512 43 + + h_bsize_ffactor 1024 43 + h_bsize_ffactor 1024 57 + h_bsize_ffactor 1024 85 + + h_bsize_ffactor 2048 85 + h_bsize_ffactor 2048 114 + h_bsize_ffactor 2048 171 + + h_bsize_ffactor 4096 171 + h_bsize_ffactor 4096 228 + h_bsize_ffactor 4096 341 + + h_bsize_ffactor 8192 341 + h_bsize_ffactor 8192 455 + h_bsize_ffactor 8192 683 + + h_bsize_ffactor 16384 341 + h_bsize_ffactor 16384 455 + h_bsize_ffactor 16384 683 + + h_bsize_ffactor 32768 341 + h_bsize_ffactor 32768 455 + h_bsize_ffactor 32768 683 + + # Begin FreeBSD + if false; then + # End FreeBSD + h_bsize_ffactor 65536 341 + h_bsize_ffactor 65536 455 + h_bsize_ffactor 65536 683 + # Begin FreeBSD + fi + # End FreeBSD +} + +# This tests 64K block size addition/removal +atf_test_case four_char_hash +four_char_hash_head() +{ + atf_set "descr" \ + "Checks hash database with 4 char key and" \ + "value insert on a 65536 bucket size" +} +four_char_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + cat >in <<EOF +p +k1234 +d1234 +r +k1234 +EOF + + # Begin FreeBSD + if true; then + atf_check "$(prog_db)" -i bsize=32768 hash in + else + # End FreeBSD + atf_check "$(prog_db)" -i bsize=65536 hash in + # Begin FreeBSD + fi + # End FreeBSD +} + + +atf_test_case bsize_torture +bsize_torture_head() +{ + atf_set "timeout" "36000" + atf_set "descr" "Checks hash database with various bucket sizes" +} +bsize_torture_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + # Begin FreeBSD + # + # db(3) doesn't support 64kB bucket sizes + for i in 2048 4096 8192 16384 32768 # 65536 + # End FreeBSD + do + atf_check "$(prog_lfsr)" $i + done +} + +atf_test_case btree_weird_page_split +btree_weird_page_split_head() +{ + atf_set "descr" \ + "Test for a weird page split condition where an insertion " \ + "into index 0 of a page that would cause the new item to " \ + "be the only item on the left page results in index 0 of " \ + "the right page being erroneously skipped; this only " \ + "happens with one particular key+data length for each page size." +} +btree_weird_page_split_body() +{ + for psize in 512 1024 2048 4096 8192; do + echo " page size $psize" + kdsizes=`awk 'BEGIN { + psize = '$psize'; hsize = int(psize/2); + for (kdsize = hsize-40; kdsize <= hsize; kdsize++) { + print kdsize; + } + }' /dev/null` + + # Use a series of keylen+datalen values in the right + # neighborhood to find the one that triggers the bug. + # We could compute the exact size that triggers the + # bug but this additional fuzz may be useful. + + # Insert keys in reverse order to maximize the chances + # for a split on index 0. + + for kdsize in $kdsizes; do + awk 'BEGIN { + kdsize = '$kdsize'; + for (i = 8; i-- > 0; ) { + s = sprintf("a%03d:%09d", i, kdsize); + for (j = 0; j < kdsize-20; j++) { + s = s "x"; + } + printf("p\nka%03d\nd%s\n", i, s); + } + print "o"; + }' /dev/null > in + sed -n 's/^d//p' in | sort > exp + atf_check -o file:exp \ + "$(prog_db)" -i psize=$psize btree in + done + done +} + +# Extremely tricky test attempting to replicate some unusual database +# corruption seen in the field: pieces of the database becoming +# inaccessible to random access, sequential access, or both. The +# hypothesis is that at least some of these are triggered by the bug +# in page splits on index 0 with a particular exact keylen+datalen. +# (See Test 40.) For psize=4096, this size is exactly 2024. + +# The order of operations here relies on very specific knowledge of +# the internals of the btree access method in order to place records +# at specific offsets in a page and to create certain keys on internal +# pages. The to-be-split page immediately prior to the bug-triggering +# split has the following properties: +# +# * is not the leftmost leaf page +# * key on the parent page is compares less than the key of the item +# on index 0 +# * triggering record's key also compares greater than the key on the +# parent page + +# Additionally, we prime the mpool LRU chain so that the head page on +# the chain has the following properties: +# +# * record at index 0 is located where it will not get overwritten by +# items written to the right-hand page during the split +# * key of the record at index 0 compares less than the key of the +# bug-triggering record + +# If the page-split bug exists, this test appears to create a database +# where some records are inaccessible to a search, but still remain in +# the file and are accessible by sequential traversal. At least one +# record gets duplicated out of sequence. + +atf_test_case btree_tricky_page_split +btree_tricky_page_split_head() +{ + atf_set "descr" \ + "btree: no unsearchables due to page split on index 0" +} +btree_tricky_page_split_body() +{ + list=`(for i in a b c d; do + for j in 990 998 999; do + echo g ${i}${j} 1024 + done + done; + echo g y997 2014 + for i in y z; do + for j in 998 999; do + echo g ${i}${j} 1024 + done + done)` + # Exact number for trigger condition accounts for newlines + # retained by dbtest with -ofile but not without; we use + # -ofile, so count newlines. keylen=5,datalen=5+2014 for + # psize=4096 here. + (cat - <<EOF +p z999 1024 +p z998 1024 +p y999 1024 +p y990 1024 +p d999 1024 +p d990 1024 +p c999 1024 +p c990 1024 +p b999 1024 +p b990 1024 +p a999 1024 +p a990 1024 +p y998 1024 +r y990 +p d998 1024 +p d990 1024 +p c998 1024 +p c990 1024 +p b998 1024 +p b990 1024 +p a998 1024 +p a990 1024 +p y997 2014 +S +o +EOF + echo "$list") | + # awk script input: + # {p|g|r} key [datasize] + awk '/^[pgr]/{ + printf("%s\nk%s\n", $1, $2); + } + /^p/{ + s = $2; + for (i = 0; i < $3; i++) { + s = s "x"; + } + printf("d%s\n", s); + } + !/^[pgr]/{ + print $0; + }' > in + (echo "$list"; echo "$list") | awk '{ + s = $2; + for (i = 0; i < $3; i++) { + s = s "x"; + } + print s; + }' > exp + atf_check -o file:exp \ + "$(prog_db)" -i psize=4096 btree in +} + +# Begin FreeBSD +if false; then +# End FreeBSD +atf_test_case btree_recursive_traversal +btree_recursive_traversal_head() +{ + atf_set "descr" \ + "btree: Test for recursive traversal successfully " \ + "retrieving records that are inaccessible to normal " \ + "sequential 'sibling-link' traversal. This works by " \ + "unlinking a few leaf pages but leaving their parent " \ + "links intact. To verify that the unlink actually makes " \ + "records inaccessible, the test first uses 'o' to do a " \ + "normal sequential traversal, followed by 'O' to do a " \ + "recursive traversal." +} +btree_recursive_traversal_body() +{ + fill="abcdefghijklmnopqrstuvwxyzy" + script='{ + for (i = 0; i < 20000; i++) { + printf("p\nkAA%05d\nd%05d%s\n", i, i, $0); + } + print "u"; + print "u"; + print "u"; + print "u"; + }' + (echo $fill | awk "$script"; echo o) > in1 + echo $fill | + awk '{ + for (i = 0; i < 20000; i++) { + if (i >= 5 && i <= 40) + continue; + printf("%05d%s\n", i, $0); + } + }' > exp1 + atf_check -o file:exp1 \ + "$(prog_db)" -i psize=512 btree in1 + echo $fill | + awk '{ + for (i = 0; i < 20000; i++) { + printf("%05d%s\n", i, $0); + } + }' > exp2 + (echo $fill | awk "$script"; echo O) > in2 + atf_check -o file:exp2 \ + "$(prog_db)" -i psize=512 btree in2 +} +# Begin FreeBSD +fi +# End FreeBSD + +atf_test_case btree_byteswap_unaligned_access_bksd +btree_byteswap_unaligned_access_bksd_head() +{ + atf_set "descr" \ + "btree: big key, small data, byteswap unaligned access" +} +btree_byteswap_unaligned_access_bksd_body() +{ + (echo foo; echo bar) | + awk '{ + s = $0 + for (i = 0; i < 488; i++) { + s = s "x"; + } + printf("p\nk%s\ndx\n", s); + }' > in + for order in 1234 4321; do + atf_check \ + "$(prog_db)" -o out -i psize=512,lorder=$order btree in + done +} + +atf_test_case btree_byteswap_unaligned_access_skbd +btree_byteswap_unaligned_access_skbd_head() +{ + atf_set "descr" \ + "btree: small key, big data, byteswap unaligned access" +} +btree_byteswap_unaligned_access_skbd_body() +{ + # 484 = 512 - 20 (header) - 7 ("foo1234") - 1 (newline) + (echo foo1234; echo bar1234) | + awk '{ + s = $0 + for (i = 0; i < 484; i++) { + s = s "x"; + } + printf("p\nk%s\nd%s\n", $0, s); + }' > in + for order in 1234 4321; do + atf_check \ + "$(prog_db)" -o out -i psize=512,lorder=$order btree in + done +} + +atf_test_case btree_known_byte_order +btree_known_byte_order_head() +{ + atf_set "descr" \ + "btree: small key, big data, known byte order" +} +btree_known_byte_order_body() +{ + local a="-i psize=512,lorder=" + + (echo foo1234; echo bar1234) | + awk '{ + s = $0 + for (i = 0; i < 484; i++) { + s = s "x"; + } + printf("%s\n", s); + }' > exp + (echo foo1234; echo bar1234) | + awk '{ + s = $0 + for (i = 0; i < 484; i++) { + s = s "x"; + } + printf("p\nk%s\nd%s\n", $0, s); + }' > in1 + for order in 1234 4321; do + atf_check \ + "$(prog_db)" -f out.$order $a$order btree in1 + done + (echo g; echo kfoo1234; echo g; echo kbar1234) > in2 + for order in 1234 4321; do + atf_check -o file:exp \ + "$(prog_db)" -s -f out.$order $a$order btree in2 + done +} + +atf_init_test_cases() +{ + atf_add_test_case small_btree + atf_add_test_case small_hash + atf_add_test_case small_recno + atf_add_test_case medium_btree + atf_add_test_case medium_hash + atf_add_test_case medium_recno + atf_add_test_case big_btree + atf_add_test_case big_hash + atf_add_test_case big_recno + atf_add_test_case random_recno + atf_add_test_case reverse_recno + atf_add_test_case alternate_recno + atf_add_test_case delete_btree + atf_add_test_case delete_recno + atf_add_test_case repeated_btree + atf_add_test_case repeated_hash + atf_add_test_case duplicate_btree + atf_add_test_case cursor_flags_btree + atf_add_test_case cursor_flags_recno + atf_add_test_case reverse_order_recno + atf_add_test_case small_page_btree + atf_add_test_case byte_orders_btree + atf_add_test_case byte_orders_hash + atf_add_test_case bsize_ffactor + atf_add_test_case four_char_hash + atf_add_test_case bsize_torture + atf_add_test_case btree_weird_page_split + atf_add_test_case btree_tricky_page_split + # Begin FreeBSD + if false; then + # End FreeBSD + atf_add_test_case btree_recursive_traversal + # Begin FreeBSD + fi + # End FreeBSD + atf_add_test_case btree_byteswap_unaligned_access_bksd + atf_add_test_case btree_byteswap_unaligned_access_skbd + atf_add_test_case btree_known_byte_order +} diff --git a/contrib/netbsd-tests/lib/libc/db/t_db_hash_seq.c b/contrib/netbsd-tests/lib/libc/db/t_db_hash_seq.c new file mode 100644 index 000000000000..6e19e22407b1 --- /dev/null +++ b/contrib/netbsd-tests/lib/libc/db/t_db_hash_seq.c @@ -0,0 +1,343 @@ +/* $NetBSD: t_db_hash_seq.c,v 1.2 2015/06/22 22:35:51 christos Exp $ */ + +/*- + * Copyright (c) 2015 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_db_hash_seq.c,v 1.2 2015/06/22 22:35:51 christos Exp $"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <db.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <syslog.h> +#include <netinet/in.h> + +#define ATF + +struct conf { + struct sockaddr_storage c_ss; + int c_lmask; + int c_port; + int c_proto; + int c_family; + int c_uid; + int c_nfail; + char c_name[128]; + int c_rmask; + int c_duration; +}; + +struct dbinfo { + int count; + time_t last; + char id[64]; +}; + +#ifdef ATF +#include <atf-c.h> + +#define DO_ERR(msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) +#define DO_WARNX(msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) +#else +#include <err.h> + +#define DO_ERR(fmt, ...) err(EXIT_FAILURE, fmt, __VA_ARGS__) +#define DO_WARNX(fmt, ...) warnx(fmt, __VA_ARGS__) +#endif + +#define DO_DEBUG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + +static HASHINFO openinfo = { + 4096, /* bsize */ + 32, /* ffactor */ + 256, /* nelem */ + 8 * 1024 * 1024,/* cachesize */ + NULL, /* hash() */ + 0 /* lorder */ +}; + +static int debug = 0; + +static int +state_close(DB *db) +{ + if (db == NULL) + return -1; + if ((*db->close)(db) == -1) + DO_ERR("%s: can't close db", __func__); + return 0; +} + +static DB * +state_open(const char *dbname, int flags, mode_t perm) +{ + DB *db; + + db = dbopen(dbname, flags, perm, DB_HASH, &openinfo); + if (db == NULL) { + if (errno == ENOENT && (flags & O_CREAT) == 0) + return NULL; + DO_ERR("%s: can't open `%s'", __func__, dbname); + } + return db; +} + +static int +state_sizecheck(const DBT *t) +{ + if (sizeof(struct conf) == t->size) + return 0; + DO_WARNX("Key size mismatch %zu != %zu", sizeof(struct conf), t->size); + return 0; +} + +static int +state_del(DB *db, const struct conf *c) +{ + int rv; + DBT k; + + if (db == NULL) + return -1; + + k.data = __UNCONST(c); + k.size = sizeof(*c); + + switch (rv = (*db->del)(db, &k, 1)) { + case 0: + case 1: + if (debug > 1) { + DO_DEBUG("%s: returns %d", __func__, rv); + (*db->sync)(db, 0); + } + return 0; + default: + DO_ERR("%s: failed", __func__); + return -1; + } +} + +#if 0 +static int +state_get(DB *db, const struct conf *c, struct dbinfo *dbi) +{ + int rv; + DBT k, v; + + if (db == NULL) + return -1; + + k.data = __UNCONST(c); + k.size = sizeof(*c); + + switch (rv = (*db->get)(db, &k, &v, 0)) { + case 0: + case 1: + if (rv) + memset(dbi, 0, sizeof(*dbi)); + else + memcpy(dbi, v.data, sizeof(*dbi)); + if (debug > 1) + DO_DEBUG("%s: returns %d", __func__, rv); + return 0; + default: + DO_ERR("%s: failed", __func__); + return -1; + } +} +#endif + +static int +state_put(DB *db, const struct conf *c, const struct dbinfo *dbi) +{ + int rv; + DBT k, v; + + if (db == NULL) + return -1; + + k.data = __UNCONST(c); + k.size = sizeof(*c); + v.data = __UNCONST(dbi); + v.size = sizeof(*dbi); + + switch (rv = (*db->put)(db, &k, &v, 0)) { + case 0: + if (debug > 1) { + DO_DEBUG("%s: returns %d", __func__, rv); + (*db->sync)(db, 0); + } + return 0; + case 1: + errno = EEXIST; + /*FALLTHROUGH*/ + default: + DO_ERR("%s: failed", __func__); + } +} + +static int +state_iterate(DB *db, struct conf *c, struct dbinfo *dbi, unsigned int first) +{ + int rv; + DBT k, v; + + if (db == NULL) + return -1; + + first = first ? R_FIRST : R_NEXT; + + switch (rv = (*db->seq)(db, &k, &v, first)) { + case 0: + if (state_sizecheck(&k) == -1) + return -1; + memcpy(c, k.data, sizeof(*c)); + memcpy(dbi, v.data, sizeof(*dbi)); + if (debug > 1) + DO_DEBUG("%s: returns %d", __func__, rv); + return 1; + case 1: + if (debug > 1) + DO_DEBUG("%s: returns %d", __func__, rv); + return 0; + default: + DO_ERR("%s: failed", __func__); + return -1; + } +} + +#define MAXB 100 + +static int +testdb(int skip) +{ + size_t i; + int f; + char flag[MAXB]; + DB *db; + struct conf c; + struct dbinfo d; + + db = state_open(NULL, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (db == NULL) + DO_ERR("%s: cannot open `%s'", __func__, "foo"); + + memset(&c, 0, sizeof(c)); + memset(&d, 0, sizeof(d)); + memset(flag, 0, sizeof(flag)); + + for (i = 0; i < __arraycount(flag); i++) { + c.c_port = i; + state_put(db, &c, &d); + } + + for (f = 1, i = 0; state_iterate(db, &c, &d, f) == 1; f = 0, i++) { + if (debug > 1) + DO_DEBUG("%zu %d\n", i, c.c_port); + if (flag[c.c_port]) + DO_WARNX("Already visited %d", c.c_port); + flag[c.c_port] = 1; + if (skip == 0 || c.c_port % skip != 0) + continue; + state_del(db, &c); + } + state_close(db); + for (i = 0; i < __arraycount(flag); i++) { + if (flag[i] == 0) + DO_WARNX("Not visited %zu", i); + } + return 0; +} + +#ifndef ATF +int +main(int argc, char *argv[]) +{ + return testdb(6); +} +#else + +ATF_TC(test_hash_del_none); +ATF_TC_HEAD(test_hash_del_none, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check sequential scan of hash tables deleting none"); +} + +ATF_TC_BODY(test_hash_del_none, tc) +{ + testdb(0); +} + +ATF_TC(test_hash_del_all); +ATF_TC_HEAD(test_hash_del_all, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check sequential scan of hash tables deleting all"); +} + +ATF_TC_BODY(test_hash_del_all, tc) +{ + testdb(1); +} + +ATF_TC(test_hash_del_alt); +ATF_TC_HEAD(test_hash_del_alt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check sequential scan of hash tables alternating deletets"); +} + +ATF_TC_BODY(test_hash_del_alt, tc) +{ + testdb(2); +} + +ATF_TC(test_hash_del_every_7); +ATF_TC_HEAD(test_hash_del_every_7, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check sequential scan of hash tables deleting every 7 elements"); +} + +ATF_TC_BODY(test_hash_del_every_7, tc) +{ + testdb(7); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, test_hash_del_none); + ATF_TP_ADD_TC(tp, test_hash_del_all); + ATF_TP_ADD_TC(tp, test_hash_del_alt); + ATF_TP_ADD_TC(tp, test_hash_del_every_7); + + return 0; +} +#endif |