summaryrefslogtreecommitdiff
path: root/libdb
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2022-01-30 00:01:51 +0000
committerColin Watson <cjwatson@debian.org>2022-01-30 00:01:51 +0000
commit106287fe531ee04ae3f6d0a793084b01659afa16 (patch)
treecabc4c3dc3d951e86887ef59eeab3e11d4b98541 /libdb
parentbf6d84c6d828db3d94d8218b2266b771ab6e5fa3 (diff)
mandb: Don't modify DB without changing its mtime
In d9ebedad15 (man-db 2.7.0), mandb started relying on the modification times of database files themselves rather than using a special row, in order to make databases reproducible between otherwise-identical installations (subject to predictable behaviour from the underlying database). There was a difficulty with this change. Because purging deleted pages from the database opened and closed the database in a separate phase before the main phase of looking for updated manual pages, and because the main phase relies on comparing against the mtime of the database to find changed files, I had to arrange to restore the original mtime at the end of the purge phase in order to avoid confusing the main phase into being a no-op. However, in some cases (perhaps due to other bugs, but nevertheless), it's possible for the purge phase to find work to do without the main phase ever finding any modified directories, which meant that the net effect of mandb would be to modify its database without changing its mtime. This is bad form, and confused some backup systems into believing that the filesystem had been corrupted, since files having their contents changed without updating their mtime is indeed a likely symptom of filesystem corruption. To avoid this, restructure our database-handling code so that a given mandb run opens any given database at most once. This means that we no longer need to modify mtimes manually, so the usual filesystem rules apply. Fixes Debian bug #1004355 and Ubuntu bug #1411633. * bootstrap.conf (gnulib_modules): Remove futimens. * libdb/db_btree.c (man_btree_close): Rename to ... (man_btree_free): ... this. Check whether wrap->file is set. Free wrap->mtime. (man_btree_new): New function. (man_btree_open): Convert to running on the result of man_btree_new. (man_btree_get_time): Cache return value in wrap->mtime. (man_btree_set_time): Remove. * libdb/db_gdbm.c (man_gdbm_new): New function. (man_gdbm_open_wrapper): Convert to running on the result of man_gdbm_new. (man_gdbm_get_time): Cache return value in wrap->mtime. (man_gdbm_set_time): Remove. (raw_close): Check whether wrap->file is set. (man_gdbm_close): Rename to ... (man_gdbm_free): ... this. * libdb/db_ndbm.c (raw_close): Check whether wrap->file is set. (man_ndbm_close): Rename to ... (man_ndbm_free): ... this. (man_ndbm_new): New function. (man_ndbm_open): Convert to running on the result of man_ndbm_new. (man_ndbm_get_time): Cache return value in wrap->mtime. (man_ndbm_set_time): Remove. * libdb/db_xdbm.c (man_xdbm_close): Rename to ... (man_xdbm_free): ... this. Update all callers. Free dbf->mtime. * libdb/db_xdbm.h (man_xdbm_close): Rename to ... (man_xdbm_free): ... this. * libdb/mydbm.h (man_gdbm_wrapper, man_ndbm_wrapper, man_btree_wrapper): Add mtime. (man_gdbm_new, man_ndbm_new, man_btree_new): Add prototypes. (man_gdbm_open_wrapper, man_ndbm_open, man_btree_open): Update prototypes. (man_gdbm_set_time, man_ndbm_set_time, man_btree_set_time): Remove prototypes. (man_gdbm_close, man_ndbm_close, man_btree_close): Rename to ... (man_gdbm_free, man_ndbm_free, man_btree_free): ... these, respectively. (MYDBM_NEW): New macro. (MYDBM_CTRWOPEN, MYDBM_RWOPEN, MYDBM_RDOPEN): Rename file argument to wrap. (MYDBM_CLOSE): Rename to ... (MYDBM_FREE): ... this. (MYDBM_SET_TIME): Remove macro. * src/catman.c (parse_for_sec): Take a MYDBM_FILE argument rather than a database path. * src/check_mandirs.c (gripe_rwopen_failed, testmandirs, create_db, update_db, purge_missing): Likewise. * src/mandb.c (update_one_file, update_db_wrapper): Likewise. * src/straycats.c (straycats): Likewise. * src/check_mandirs.h (create_db, update_db, purge_missing): Update prototypes. * src/straycats.h (straycats): Likewise. * src/check_mandirs.c (ensure_db_open): New function. (testmandirs): Only open the database if it wasn't already open, and don't close it. (update_db, purge_missing): Likewise. * src/mandb.c (update_one_file): Likewise. * src/straycats.c (straycats): Assert that the database is already open, and don't close it. * src/accessdb.c (main): Update database opening code. * src/catman.c (post_fork): Update database closing code. (parse_for_sec): Move database opening and closing code to ... (main): ... here, and update it. * src/man.c (dbdelete_wrapper): Update database opening and closing code. * src/whatis.c (search): Update database opening and closing code. * src/check_mandirs.c (purge_missing): Don't reorganize the database. * src/mandb.c (mandb): Create a MYDBM_FILE from the database path, and close and free it before returning. Rearrange purging so that it runs on the temporary database copy, which we close and remove if purge_missing finds a consistency problem requiring us to rescan from scratch. Unless purging requires a rescan, all of the purge, main, and stray cats phases now run on the same open database file. Reorganize the database before closing it if the purge phase did any work. (process_manpath): Rename the temporary database into place if either of the purge or stray cats phases did any work, even if the main phase didn't. * src/check_mandirs.c (update_db_time): Remove. (create_db): Don't manually update the database mtime. (update_db, purge_missing): Likewise. * src/tests/Makefile.am (ALL_TESTS): Add mandb-purge-updates-timestamp. (check_PROGRAMS): Add get-mtime. (get_mtime_SOURCES, get_mtime_LDADD): Add. * src/tests/get-mtime.c: New file: test helper to get a file's mtime portably. * src/tests/mandb-purge-updates-timestamp: New file. * .gitignore: Add src/tests/get-mtime. * NEWS: Document this.
Diffstat (limited to 'libdb')
-rw-r--r--libdb/db_btree.c85
-rw-r--r--libdb/db_gdbm.c59
-rw-r--r--libdb/db_ndbm.c87
-rw-r--r--libdb/db_xdbm.c3
-rw-r--r--libdb/db_xdbm.h2
-rw-r--r--libdb/mydbm.h61
6 files changed, 152 insertions, 145 deletions
diff --git a/libdb/db_btree.c b/libdb/db_btree.c
index 27edd03b..54242c00 100644
--- a/libdb/db_btree.c
+++ b/libdb/db_btree.c
@@ -28,6 +28,7 @@
/* below this line are routines only useful for the BTREE interface */
#ifdef BTREE
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
@@ -64,22 +65,35 @@ gl_set_t loop_check;
blocking method is used ": Try again". This adopts GNU dbm's approach. */
/* release the lock and close the database */
-void man_btree_close (man_btree_wrapper wrap)
+void man_btree_free (man_btree_wrapper wrap)
{
if (!wrap)
return;
free (wrap->name);
- (void) flock ((wrap->file->fd) (wrap->file), LOCK_UN);
- (wrap->file->close) (wrap->file);
+ if (wrap->file) {
+ (void) flock ((wrap->file->fd) (wrap->file), LOCK_UN);
+ (wrap->file->close) (wrap->file);
+ }
+ free (wrap->mtime);
free (wrap);
}
-/* open a btree type database, with file locking. */
-man_btree_wrapper man_btree_open (const char *name, int flags, int mode)
+man_btree_wrapper man_btree_new (const char *name)
{
man_btree_wrapper wrap;
- DB *file;
+
+ wrap = xmalloc (sizeof *wrap);
+ wrap->name = xstrdup (name);
+ wrap->file = NULL;
+ wrap->mtime = NULL;
+
+ return wrap;
+}
+
+/* open a btree type database, with file locking. */
+bool man_btree_open (man_btree_wrapper wrap, int flags, int mode)
+{
BTREEINFO b;
int lock_op;
int lock_failed;
@@ -111,11 +125,11 @@ man_btree_wrapper man_btree_open (const char *name, int flags, int mode)
* and ignore the database if it's zero-length.
*/
struct stat iszero;
- if (stat (name, &iszero) < 0)
- return NULL;
+ if (stat (wrap->name, &iszero) < 0)
+ return false;
if (iszero.st_size == 0) {
errno = EINVAL;
- return NULL;
+ return false;
}
}
@@ -123,35 +137,32 @@ man_btree_wrapper man_btree_open (const char *name, int flags, int mode)
/* opening the db is destructive, need to lock first */
int fd;
- file = NULL;
+ wrap->file = NULL;
lock_failed = 1;
- fd = open (name, flags & ~O_TRUNC, mode);
+ fd = open (wrap->name, flags & ~O_TRUNC, mode);
if (fd != -1) {
if (!(lock_failed = flock (fd, lock_op)))
- file = dbopen (name, flags, mode,
- DB_BTREE, &b);
+ wrap->file = dbopen (wrap->name, flags, mode,
+ DB_BTREE, &b);
close (fd);
}
} else {
- file = dbopen (name, flags, mode, DB_BTREE, &b);
- if (file)
- lock_failed = flock ((file->fd) (file), lock_op);
+ wrap->file = dbopen (wrap->name, flags, mode, DB_BTREE, &b);
+ if (wrap->file)
+ lock_failed = flock ((wrap->file->fd) (wrap->file),
+ lock_op);
}
- if (!file)
- return NULL;
+ if (!wrap->file)
+ return false;
if (lock_failed) {
- gripe_lock (name);
- (file->close) (file);
- return NULL;
+ gripe_lock (wrap->name);
+ (wrap->file->close) (wrap->file);
+ return false;
}
- wrap = xmalloc (sizeof *wrap);
- wrap->name = xstrdup (name);
- wrap->file = file;
-
- return wrap;
+ return true;
}
/* do a replace when we have the duplicate flag set on the database -
@@ -267,22 +278,16 @@ struct timespec man_btree_get_time (man_btree_wrapper wrap)
{
struct stat st;
- if (fstat ((wrap->file->fd) (wrap->file), &st) < 0) {
- struct timespec t;
- t.tv_sec = -1;
- t.tv_nsec = -1;
- return t;
+ if (!wrap->mtime) {
+ wrap->mtime = XMALLOC (struct timespec);
+ if (fstat ((wrap->file->fd) (wrap->file), &st) < 0) {
+ wrap->mtime->tv_sec = -1;
+ wrap->mtime->tv_nsec = -1;
+ } else
+ *wrap->mtime = get_stat_mtime (&st);
}
- return get_stat_mtime (&st);
-}
-
-void man_btree_set_time (man_btree_wrapper wrap, const struct timespec time)
-{
- struct timespec times[2];
- times[0] = time;
- times[1] = time;
- futimens ((wrap->file->fd) (wrap->file), times);
+ return *wrap->mtime;
}
#endif /* BTREE */
diff --git a/libdb/db_gdbm.c b/libdb/db_gdbm.c
index ef381c7b..8179d8d4 100644
--- a/libdb/db_gdbm.c
+++ b/libdb/db_gdbm.c
@@ -26,6 +26,7 @@
#ifdef GDBM
+#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@@ -65,22 +66,29 @@ static void trap_error (const char *val)
fprintf (stderr, "gdbm fatal: %s\n", val);
}
-man_gdbm_wrapper man_gdbm_open_wrapper (const char *name, int flags)
+man_gdbm_wrapper man_gdbm_new (const char *name)
{
man_gdbm_wrapper wrap;
- GDBM_FILE file;
+
+ wrap = xmalloc (sizeof *wrap);
+ wrap->name = xstrdup (name);
+ wrap->file = NULL;
+ wrap->mtime = NULL;
+
+ return wrap;
+}
+
+bool man_gdbm_open_wrapper (man_gdbm_wrapper wrap, int flags)
+{
datum key, content;
opening = 1;
if (setjmp (open_env))
- return NULL;
- file = gdbm_open ((char *) name, BLK_SIZE, flags, DBMODE, trap_error);
- if (!file)
- return NULL;
-
- wrap = xmalloc (sizeof *wrap);
- wrap->name = xstrdup (name);
- wrap->file = file;
+ return false;
+ wrap->file = gdbm_open (wrap->name, BLK_SIZE, flags, DBMODE,
+ trap_error);
+ if (!wrap->file)
+ return false;
if ((flags & ~GDBM_FAST) != GDBM_NEWDB) {
/* While the setjmp/longjmp guard is in effect, make sure we
@@ -95,7 +103,7 @@ man_gdbm_wrapper man_gdbm_open_wrapper (const char *name, int flags)
opening = 0;
- return wrap;
+ return true;
}
static datum unsorted_firstkey (man_gdbm_wrapper wrap)
@@ -122,32 +130,27 @@ struct timespec man_gdbm_get_time (man_gdbm_wrapper wrap)
{
struct stat st;
- if (fstat (gdbm_fdesc (wrap->file), &st) < 0) {
- struct timespec t;
- t.tv_sec = -1;
- t.tv_nsec = -1;
- return t;
+ if (!wrap->mtime) {
+ wrap->mtime = XMALLOC (struct timespec);
+ if (fstat (gdbm_fdesc (wrap->file), &st) < 0) {
+ wrap->mtime->tv_sec = -1;
+ wrap->mtime->tv_nsec = -1;
+ } else
+ *wrap->mtime = get_stat_mtime (&st);
}
- return get_stat_mtime (&st);
-}
-
-void man_gdbm_set_time (man_gdbm_wrapper wrap, const struct timespec time)
-{
- struct timespec times[2];
- times[0] = time;
- times[1] = time;
- futimens (gdbm_fdesc (wrap->file), times);
+ return *wrap->mtime;
}
static void raw_close (man_gdbm_wrapper wrap)
{
- gdbm_close (wrap->file);
+ if (wrap->file)
+ gdbm_close (wrap->file);
}
-void man_gdbm_close (man_gdbm_wrapper wrap)
+void man_gdbm_free (man_gdbm_wrapper wrap)
{
- man_xdbm_close (wrap, raw_close);
+ man_xdbm_free (wrap, raw_close);
}
#ifndef HAVE_GDBM_EXISTS
diff --git a/libdb/db_ndbm.c b/libdb/db_ndbm.c
index ed6ca3a8..b447e656 100644
--- a/libdb/db_ndbm.c
+++ b/libdb/db_ndbm.c
@@ -26,6 +26,7 @@
#ifdef NDBM
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
@@ -50,21 +51,32 @@
/* release the lock and close the database */
static void raw_close (man_ndbm_wrapper wrap)
{
- flock (dbm_dirfno (wrap->file), LOCK_UN);
- dbm_close (wrap->file);
+ if (wrap->file) {
+ flock (dbm_dirfno (wrap->file), LOCK_UN);
+ dbm_close (wrap->file);
+ }
}
-void man_ndbm_close (man_ndbm_wrapper wrap)
+void man_ndbm_free (man_ndbm_wrapper wrap)
{
- man_xdbm_close (wrap, raw_close);
+ man_xdbm_free (wrap, raw_close);
}
-/* open a ndbm type database, with file locking. */
-man_ndbm_wrapper man_ndbm_open (const char *name, int flags, int mode)
+man_ndbm_wrapper man_ndbm_new (const char *name)
{
man_ndbm_wrapper wrap;
- char *name_copy;
- DBM *file;
+
+ wrap = xmalloc (sizeof *wrap);
+ wrap->name = xstrdup (name);
+ wrap->file = NULL;
+ wrap->mtime = NULL;
+
+ return wrap;
+}
+
+/* open a ndbm type database, with file locking. */
+bool man_ndbm_open (man_ndbm_wrapper wrap, int flags, int mode)
+{
int lock_op;
int lock_failed;
@@ -75,49 +87,38 @@ man_ndbm_wrapper man_ndbm_open (const char *name, int flags, int mode)
lock_op = LOCK_SH | LOCK_NB;
}
- /* At least GDBM's version of dbm_open declares the file name
- * parameter as non-const. This is probably incorrect, but take a
- * copy just in case.
- */
- name_copy = xstrdup (name);
-
if (flags & O_TRUNC) {
/* opening the db is destructive, need to lock first */
char *dir_fname;
int dir_fd;
- file = NULL;
+ wrap->file = NULL;
lock_failed = 1;
- dir_fname = xasprintf ("%s.dir", name);
+ dir_fname = xasprintf ("%s.dir", wrap->name);
dir_fd = open (dir_fname, flags & ~O_TRUNC, mode);
free (dir_fname);
if (dir_fd != -1) {
if (!(lock_failed = flock (dir_fd, lock_op)))
- file = dbm_open (name_copy, flags, mode);
+ wrap->file = dbm_open (wrap->name, flags,
+ mode);
close (dir_fd);
}
} else {
- file = dbm_open (name_copy, flags, mode);
- if (file)
- lock_failed = flock (dbm_dirfno (file), lock_op);
+ wrap->file = dbm_open (wrap->name, flags, mode);
+ if (wrap->file)
+ lock_failed = flock (dbm_dirfno (wrap->file), lock_op);
}
- free (name_copy);
-
- if (!file)
- return NULL;
+ if (!wrap->file)
+ return false;
if (lock_failed) {
- gripe_lock (name);
- dbm_close (file);
- return NULL;
+ gripe_lock (wrap->name);
+ dbm_close (wrap->file);
+ return false;
}
- wrap = xmalloc (sizeof *wrap);
- wrap->name = xstrdup (name);
- wrap->file = file;
-
- return wrap;
+ return true;
}
static datum unsorted_firstkey (man_ndbm_wrapper wrap)
@@ -144,22 +145,16 @@ struct timespec man_ndbm_get_time (man_ndbm_wrapper wrap)
{
struct stat st;
- if (fstat (dbm_dirfno (wrap->file), &st) < 0) {
- struct timespec t;
- t.tv_sec = -1;
- t.tv_nsec = -1;
- return t;
+ if (!wrap->mtime) {
+ wrap->mtime = XMALLOC (struct timespec);
+ if (fstat (dbm_dirfno (wrap->file), &st) < 0) {
+ wrap->mtime->tv_sec = -1;
+ wrap->mtime->tv_nsec = -1;
+ } else
+ *wrap->mtime = get_stat_mtime (&st);
}
- return get_stat_mtime (&st);
-}
-
-void man_ndbm_set_time (man_ndbm_wrapper wrap, const struct timespec time)
-{
- struct timespec times[2];
- times[0] = time;
- times[1] = time;
- futimens (dbm_dirfno (wrap->file), times);
+ return *wrap->mtime;
}
#endif /* NDBM */
diff --git a/libdb/db_xdbm.c b/libdb/db_xdbm.c
index 95a7ffc8..14a70e2c 100644
--- a/libdb/db_xdbm.c
+++ b/libdb/db_xdbm.c
@@ -154,7 +154,7 @@ datum man_xdbm_nextkey (MYDBM_FILE dbf, datum key)
return copy_datum (*(datum *) gl_list_node_value (keys, next_node));
}
-void man_xdbm_close (MYDBM_FILE dbf, man_xdbm_raw_close raw_close)
+void man_xdbm_free (MYDBM_FILE dbf, man_xdbm_raw_close raw_close)
{
if (!dbf)
return;
@@ -164,6 +164,7 @@ void man_xdbm_close (MYDBM_FILE dbf, man_xdbm_raw_close raw_close)
free (dbf->name);
raw_close (dbf);
+ free (dbf->mtime);
free (dbf);
}
diff --git a/libdb/db_xdbm.h b/libdb/db_xdbm.h
index 9d7f4bf7..2d16d165 100644
--- a/libdb/db_xdbm.h
+++ b/libdb/db_xdbm.h
@@ -33,7 +33,7 @@ datum man_xdbm_firstkey (MYDBM_FILE dbf,
man_xdbm_unsorted_firstkey firstkey,
man_xdbm_unsorted_nextkey nextkey);
datum man_xdbm_nextkey (MYDBM_FILE dbf, datum key);
-void man_xdbm_close (MYDBM_FILE dbf, man_xdbm_raw_close raw_close);
+void man_xdbm_free (MYDBM_FILE dbf, man_xdbm_raw_close raw_close);
#endif /* GDBM || NDBM */
diff --git a/libdb/mydbm.h b/libdb/mydbm.h
index 208e1ae7..8b177c3a 100644
--- a/libdb/mydbm.h
+++ b/libdb/mydbm.h
@@ -37,6 +37,8 @@
#ifndef MYDBM_H
# define MYDBM_H
+# include <stdbool.h>
+
# include "timespec.h"
# include "xvasprintf.h"
@@ -54,37 +56,38 @@ extern int gdbm_exists (GDBM_FILE db, datum key);
typedef struct {
char *name;
GDBM_FILE file;
+ struct timespec *mtime;
} *man_gdbm_wrapper;
-man_gdbm_wrapper man_gdbm_open_wrapper (const char *name, int flags);
+man_gdbm_wrapper man_gdbm_new (const char *name);
+bool man_gdbm_open_wrapper (man_gdbm_wrapper wrap, int flags);
datum man_gdbm_firstkey (man_gdbm_wrapper wrap);
datum man_gdbm_nextkey (man_gdbm_wrapper wrap, datum key);
struct timespec man_gdbm_get_time (man_gdbm_wrapper wrap);
-void man_gdbm_set_time (man_gdbm_wrapper wrap, const struct timespec time);
-void man_gdbm_close (man_gdbm_wrapper wrap);
+void man_gdbm_free (man_gdbm_wrapper wrap);
# define BLK_SIZE 0 /* to invoke normal fs block size */
# define DB_EXT ".db"
# define MYDBM_FILE man_gdbm_wrapper
+# define MYDBM_NEW(file) man_gdbm_new(file)
# define MYDBM_DPTR(d) ((d).dptr)
# define MYDBM_SET_DPTR(d, value) ((d).dptr = (value))
# define MYDBM_DSIZE(d) ((d).dsize)
-# define MYDBM_CTRWOPEN(file) \
- man_gdbm_open_wrapper(file, GDBM_NEWDB|GDBM_FAST)
-# define MYDBM_RWOPEN(file) \
- man_gdbm_open_wrapper(file, GDBM_WRITER|GDBM_FAST)
-# define MYDBM_RDOPEN(file) \
- man_gdbm_open_wrapper(file, GDBM_READER)
+# define MYDBM_CTRWOPEN(wrap) \
+ man_gdbm_open_wrapper(wrap, GDBM_NEWDB|GDBM_FAST)
+# define MYDBM_RWOPEN(wrap) \
+ man_gdbm_open_wrapper(wrap, GDBM_WRITER|GDBM_FAST)
+# define MYDBM_RDOPEN(wrap) \
+ man_gdbm_open_wrapper(wrap, GDBM_READER)
# define MYDBM_INSERT(db, key, cont) gdbm_store((db)->file, key, cont, GDBM_INSERT)
# define MYDBM_REPLACE(db, key, cont) gdbm_store((db)->file, key, cont, GDBM_REPLACE)
# define MYDBM_EXISTS(db, key) gdbm_exists((db)->file, key)
# define MYDBM_DELETE(db, key) gdbm_delete((db)->file, key)
# define MYDBM_FETCH(db, key) gdbm_fetch((db)->file, key)
-# define MYDBM_CLOSE(db) man_gdbm_close(db)
+# define MYDBM_FREE(db) man_gdbm_free(db)
# define MYDBM_FIRSTKEY(db) man_gdbm_firstkey(db)
# define MYDBM_NEXTKEY(db, key) man_gdbm_nextkey(db, key)
# define MYDBM_GET_TIME(db) man_gdbm_get_time(db)
-# define MYDBM_SET_TIME(db, time) man_gdbm_set_time(db, time)
# define MYDBM_REORG(db) gdbm_reorganize((db)->file)
# elif defined(NDBM) && !defined(GDBM) && !defined(BTREE)
@@ -100,33 +103,34 @@ void man_gdbm_close (man_gdbm_wrapper wrap);
typedef struct {
char *name;
DBM *file;
+ struct timespec *mtime;
} *man_ndbm_wrapper;
-extern man_ndbm_wrapper man_ndbm_open (const char *name, int flags, int mode);
+extern man_ndbm_wrapper man_ndbm_new (const char *name);
+extern bool man_ndbm_open (man_ndbm_wrapper wrap, int flags, int mode);
extern datum man_ndbm_firstkey (man_ndbm_wrapper wrap);
extern datum man_ndbm_nextkey (man_ndbm_wrapper wrap, datum key);
extern struct timespec man_ndbm_get_time (man_ndbm_wrapper wrap);
-extern void man_ndbm_set_time (man_ndbm_wrapper wrap, const struct timespec time);
-extern void man_ndbm_close (man_ndbm_wrapper wrap);
+extern void man_ndbm_free (man_ndbm_wrapper wrap);
# define DB_EXT ""
# define MYDBM_FILE man_ndbm_wrapper
+# define MYDBM_NEW(file) man_ndbm_new(file)
# define MYDBM_DPTR(d) ((d).dptr)
# define MYDBM_SET_DPTR(d, value) ((d).dptr = (value))
# define MYDBM_DSIZE(d) ((d).dsize)
-# define MYDBM_CTRWOPEN(file) man_ndbm_open(file, O_TRUNC|O_CREAT|O_RDWR, DBMODE)
-# define MYDBM_RWOPEN(file) man_ndbm_open(file, O_RDWR, DBMODE)
-# define MYDBM_RDOPEN(file) man_ndbm_open(file, O_RDONLY, DBMODE)
+# define MYDBM_CTRWOPEN(wrap) man_ndbm_open(wrap, O_TRUNC|O_CREAT|O_RDWR, DBMODE)
+# define MYDBM_RWOPEN(wrap) man_ndbm_open(wrap, O_RDWR, DBMODE)
+# define MYDBM_RDOPEN(wrap) man_ndbm_open(wrap, O_RDONLY, DBMODE)
# define MYDBM_INSERT(db, key, cont) dbm_store((db)->file, key, cont, DBM_INSERT)
# define MYDBM_REPLACE(db, key, cont) dbm_store((db)->file, key, cont, DBM_REPLACE)
# define MYDBM_EXISTS(db, key) (dbm_fetch((db)->file, key).dptr != NULL)
# define MYDBM_DELETE(db, key) dbm_delete((db)->file, key)
# define MYDBM_FETCH(db, key) copy_datum(dbm_fetch((db)->file, key))
-# define MYDBM_CLOSE(db) man_ndbm_close(db)
+# define MYDBM_FREE(db) man_ndbm_free(db)
# define MYDBM_FIRSTKEY(db) man_ndbm_firstkey(db)
# define MYDBM_NEXTKEY(db, key) man_ndbm_nextkey(db, key)
# define MYDBM_GET_TIME(db) man_ndbm_get_time(db)
-# define MYDBM_SET_TIME(db, time) man_ndbm_set_time(db, time)
# define MYDBM_REORG(db) /* nothing - not implemented */
# elif defined(BTREE) && !defined(NDBM) && !defined(GDBM)
@@ -139,13 +143,14 @@ extern void man_ndbm_close (man_ndbm_wrapper wrap);
typedef struct {
char *name;
DB *file;
+ struct timespec *mtime;
} *man_btree_wrapper;
typedef DBT datum;
-extern man_btree_wrapper man_btree_open (const char *filename, int flags,
- int mode);
-extern void man_btree_close (man_btree_wrapper wrap);
+extern man_btree_wrapper man_btree_new (const char *filename);
+extern bool man_btree_open (man_btree_wrapper wrap, int flags, int mode);
+extern void man_btree_free (man_btree_wrapper wrap);
extern int man_btree_exists (man_btree_wrapper wrap, datum key);
extern datum man_btree_fetch (man_btree_wrapper wrap, datum key);
extern int man_btree_insert (man_btree_wrapper wrap, datum key, datum cont);
@@ -156,27 +161,25 @@ extern int man_btree_replace (man_btree_wrapper wrap,
extern int man_btree_nextkeydata (man_btree_wrapper wrap,
datum *key, datum *cont);
extern struct timespec man_btree_get_time (man_btree_wrapper wrap);
-extern void man_btree_set_time (man_btree_wrapper wrap,
- const struct timespec time);
# define DB_EXT ".bt"
# define MYDBM_FILE man_btree_wrapper
+# define MYDBM_NEW(file) man_btree_new(file)
# define MYDBM_DPTR(d) ((char *) (d).data)
# define MYDBM_SET_DPTR(d, value) ((d).data = (char *) (value))
# define MYDBM_DSIZE(d) ((d).size)
-# define MYDBM_CTRWOPEN(file) man_btree_open(file, O_TRUNC|O_CREAT|O_RDWR, DBMODE)
-# define MYDBM_RWOPEN(file) man_btree_open(file, O_RDWR, DBMODE)
-# define MYDBM_RDOPEN(file) man_btree_open(file, O_RDONLY, DBMODE)
+# define MYDBM_CTRWOPEN(wrap) man_btree_open(wrap, O_TRUNC|O_CREAT|O_RDWR, DBMODE)
+# define MYDBM_RWOPEN(wrap) man_btree_open(wrap, O_RDWR, DBMODE)
+# define MYDBM_RDOPEN(wrap) man_btree_open(wrap, O_RDONLY, DBMODE)
# define MYDBM_INSERT(db, key, cont) man_btree_insert(db, key, cont)
# define MYDBM_REPLACE(db, key, cont) man_btree_replace(db, key, cont)
# define MYDBM_EXISTS(db, key) man_btree_exists(db, key)
# define MYDBM_DELETE(db, key) (((db)->file->del)((db)->file, &key, 0) ? -1 : 0)
# define MYDBM_FETCH(db, key) man_btree_fetch(db, key)
-# define MYDBM_CLOSE(db) man_btree_close(db)
+# define MYDBM_FREE(db) man_btree_free(db)
# define MYDBM_FIRSTKEY(db) man_btree_firstkey(db)
# define MYDBM_NEXTKEY(db, key) man_btree_nextkey(db)
# define MYDBM_GET_TIME(db) man_btree_get_time(db)
-# define MYDBM_SET_TIME(db, time) man_btree_set_time(db, time)
# define MYDBM_REORG(db) /* nothing - not implemented */
# else /* not GDBM or NDBM or BTREE */