diff options
Diffstat (limited to 'db2/log')
-rw-r--r-- | db2/log/log.c | 438 | ||||
-rw-r--r-- | db2/log/log.src | 53 | ||||
-rw-r--r-- | db2/log/log_archive.c | 413 | ||||
-rw-r--r-- | db2/log/log_auto.c | 351 | ||||
-rw-r--r-- | db2/log/log_compare.c | 34 | ||||
-rw-r--r-- | db2/log/log_findckp.c | 130 | ||||
-rw-r--r-- | db2/log/log_get.c | 355 | ||||
-rw-r--r-- | db2/log/log_put.c | 484 | ||||
-rw-r--r-- | db2/log/log_rec.c | 332 | ||||
-rw-r--r-- | db2/log/log_register.c | 199 |
10 files changed, 2789 insertions, 0 deletions
diff --git a/db2/log/log.c b/db2/log/log.c new file mode 100644 index 0000000000..1684ce8cc8 --- /dev/null +++ b/db2/log/log.c @@ -0,0 +1,438 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log.c 10.24 (Sleepycat) 8/16/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "db_shash.h" +#include "log.h" +#include "db_dispatch.h" +#include "txn_auto.h" +#include "common_ext.h" + +static int __log_recover __P((DB_ENV *, DB_LOG *)); + +/* + * log_open -- + * Initialize and/or join a log. + */ +int +log_open(path, flags, mode, dbenv, lpp) + const char *path; + int flags; + int mode; + DB_ENV *dbenv; + DB_LOG **lpp; +{ + DB_LOG *dblp; + LOG *lp; + size_t len; + int fd, newregion, ret, retry_cnt; + + /* Validate arguments. */ +#ifdef HAVE_SPINLOCKS +#define OKFLAGS (DB_CREATE | DB_THREAD) +#else +#define OKFLAGS (DB_CREATE) +#endif + if ((ret = __db_fchk(dbenv, "log_open", flags, OKFLAGS)) != 0) + return (ret); + + /* + * We store 4-byte offsets into the file, so the maximum file + * size can't be larger than that. + */ + if (dbenv != NULL && dbenv->lg_max > UINT32_T_MAX) { + __db_err(dbenv, "log_open: maximum file size too large"); + return (EINVAL); + } + + /* Create and initialize the DB_LOG structure. */ + if ((dblp = (DB_LOG *)calloc(1, sizeof(DB_LOG))) == NULL) + return (ENOMEM); + + dblp->dbenv = dbenv; + dblp->lfd = -1; + ZERO_LSN(dblp->c_lsn); + dblp->c_fd = -1; + if (LF_ISSET(DB_THREAD)) { + F_SET(dblp, DB_AM_THREAD); + (void)__db_mutex_init(&dblp->mutex, -1); + } + + /* + * The log region isn't fixed size because we store the registered + * file names there. Make it fairly large so that we don't have to + * grow it. + */ + len = 30 * 1024; + + /* Map in the region. */ + retry_cnt = newregion = 0; +retry: if (LF_ISSET(DB_CREATE)) { + ret = __db_rcreate(dbenv, DB_APP_LOG, path, + DB_DEFAULT_LOG_FILE, mode, len, &fd, &dblp->maddr); + if (ret == 0) { + /* Put the LOG structure first in the region. */ + lp = dblp->maddr; + + /* Initialize the rest of the region as free space. */ + dblp->addr = (u_int8_t *)dblp->maddr + sizeof(LOG); + __db_shalloc_init(dblp->addr, len - sizeof(LOG)); + + /* Initialize the LOG structure. */ + lp->persist.lg_max = dbenv == NULL ? 0 : dbenv->lg_max; + if (lp->persist.lg_max == 0) + lp->persist.lg_max = DEFAULT_MAX; + lp->persist.magic = DB_LOGMAGIC; + lp->persist.version = DB_LOGVERSION; + lp->persist.mode = mode; + SH_TAILQ_INIT(&lp->fq); + + /* Initialize LOG LSNs. */ + lp->lsn.file = 1; + lp->lsn.offset = 0; + + newregion = 1; + } else if (ret != EEXIST) + return (ret); + } + + /* If we didn't or couldn't create the region, try and join it. */ + if (!newregion && + (ret = __db_ropen(dbenv, DB_APP_LOG, + path, DB_DEFAULT_LOG_FILE, 0, &fd, &dblp->maddr)) != 0) { + /* + * If we fail because the file isn't available, wait a + * second and try again. + */ + if (ret == EAGAIN && ++retry_cnt < 3) { + (void)__db_sleep(1, 0); + goto retry; + } + return (ret); + } + + /* Set up the common information. */ + dblp->lp = dblp->maddr; + dblp->addr = (u_int8_t *)dblp->maddr + sizeof(LOG); + dblp->fd = fd; + + /* + * If doing recovery, try and recover any previous log files + * before releasing the lock. + */ + if (newregion) { + if ((ret = __log_recover(dbenv, dblp)) != 0) { + log_unlink(path, 1, dbenv); + return (ret); + } + UNLOCK_LOGREGION(dblp); + } + *lpp = dblp; + return (0); +} + +/* + * __log_recover -- + * Recover a log. + */ +static int +__log_recover(dbenv, dblp) + DB_ENV *dbenv; + DB_LOG *dblp; +{ + DBT dbt; + DB_LSN lsn; + LOG *lp; + u_int32_t chk; + int cnt, found_checkpoint, ret; + + lp = dblp->lp; + + /* + * Find a log file. If none exist, we simply return, leaving + * everything initialized to a new log. + */ + if ((ret = __log_find(dbenv, lp, &cnt)) != 0) + return (ret); + if (cnt == 0) + return (0); + + /* We have a log file name, find the last one. */ + while (cnt < MAXLFNAME) + if (__log_valid(dbenv, lp, ++cnt) != 0) { + --cnt; + break; + } + + /* + * We have the last useful log file and we've loaded any persistent + * information. Pretend that the log is larger than it can possibly + * be, and read this file, looking for a checkpoint and its end. + */ + dblp->c_lsn.file = cnt; + dblp->c_lsn.offset = 0; + lsn = dblp->c_lsn; + lp->lsn.file = cnt + 1; + lp->lsn.offset = 0; + + /* Set the cursor. Shouldn't fail, leave error messages on. */ + memset(&dbt, 0, sizeof(dbt)); + if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0) + return (ret); + + /* + * Read to the end of the file, saving checkpoints. This will fail + * at some point, so turn off error messages. + */ + found_checkpoint = 0; + while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) { + if (dbt.size < sizeof(u_int32_t)) + continue; + memcpy(&chk, dbt.data, sizeof(u_int32_t)); + if (chk == DB_txn_ckp) { + lp->c_lsn = lsn; + found_checkpoint = 1; + } + } + + /* + * We know where the end of the log is. Since that record is on disk, + * it's also the last-synced LSN. + */ + lp->lsn = lsn; + lp->lsn.offset += dblp->c_len; + lp->s_lsn = lp->lsn; + + /* Set up the current buffer information, too. */ + lp->len = dblp->c_len; + lp->b_off = 0; + lp->w_off = lp->lsn.offset; + + /* + * It's possible that we didn't find a checkpoint because there wasn't + * one in the last log file. Start searching. + */ + while (!found_checkpoint && cnt > 1) { + dblp->c_lsn.file = --cnt; + dblp->c_lsn.offset = 0; + lsn = dblp->c_lsn; + + /* Set the cursor. Shouldn't fail, leave error messages on. */ + if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0) + return (ret); + + /* + * Read to the end of the file, saving checkpoints. Shouldn't + * fail, leave error messages on. + */ + while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 0) == 0) { + if (dbt.size < sizeof(u_int32_t)) + continue; + memcpy(&chk, dbt.data, sizeof(u_int32_t)); + if (chk == DB_txn_ckp) { + lp->c_lsn = lsn; + found_checkpoint = 1; + } + } + } + + /* If we never find a checkpoint, that's okay, just 0 it out. */ + if (!found_checkpoint) { + lp->c_lsn.file = 1; + lp->c_lsn.offset = 0; + } + + __db_err(dbenv, + "Recovering the log: last valid LSN: file: %lu offset %lu", + (u_long)lp->lsn.file, (u_long)lp->lsn.offset); + + /* Reset the cursor. */ + ZERO_LSN(dblp->c_lsn); + + return (0); +} + +/* + * __log_find -- + * Try to find a log file. + * + * PUBLIC: int __log_find __P((DB_ENV *, LOG *, int *)); + */ +int +__log_find(dbenv, lp, valp) + DB_ENV *dbenv; + LOG *lp; + int *valp; +{ + int cnt, fcnt, logval, ret; + const char *dir; + char **names, *p, *q; + + /* Find the directory name. */ + if ((ret = __log_name(dbenv, 1, &p)) != 0) + return (ret); + if ((q = __db_rpath(p)) == NULL) + dir = PATH_DOT; + else { + *q = '\0'; + dir = p; + } + + /* Get the list of file names. */ + ret = __db_dir(dbenv, dir, &names, &fcnt); + FREES(p); + if (ret != 0) + return (ret); + + /* + * Search for a valid log file name, return a value of 0 on + * failure. + */ + *valp = 0; + for (cnt = fcnt, logval = 0; --cnt >= 0;) + if (strncmp(names[cnt], "log.", sizeof("log.") - 1) == 0) { + logval = atoi(names[cnt] + 4); + if (logval != 0 && + __log_valid(dbenv, lp, logval) == 0) { + *valp = logval; + break; + } + } + + /* Discard the list. */ + __db_dirf(dbenv, names, fcnt); + + return (ret); +} + +/* + * log_valid -- + * Validate a log file. + * + * PUBLIC: int __log_valid __P((DB_ENV *, LOG *, int)); + */ +int +__log_valid(dbenv, lp, cnt) + DB_ENV *dbenv; + LOG *lp; + int cnt; +{ + LOGP persist; + ssize_t nw; + int fd, ret; + char *p; + + if ((ret = __log_name(dbenv, cnt, &p)) != 0) + return (ret); + + fd = -1; + if ((ret = __db_fdopen(p, + DB_RDONLY | DB_SEQUENTIAL, + DB_RDONLY | DB_SEQUENTIAL, 0, &fd)) != 0 || + (ret = __db_lseek(fd, 0, 0, sizeof(HDR), SEEK_SET)) != 0 || + (ret = __db_read(fd, &persist, sizeof(LOGP), &nw)) != 0 || + nw != sizeof(LOGP)) { + if (ret == 0) + ret = EIO; + if (fd != -1) { + (void)__db_close(fd); + __db_err(dbenv, + "Ignoring log file: %s: %s", p, strerror(ret)); + } + goto err; + } + (void)__db_close(fd); + + if (persist.magic != DB_LOGMAGIC) { + __db_err(dbenv, + "Ignoring log file: %s: magic number %lx, not %lx", + p, (u_long)persist.magic, (u_long)DB_LOGMAGIC); + ret = EINVAL; + goto err; + } + if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) { + __db_err(dbenv, + "Ignoring log file: %s: unsupported log version %lu", + p, (u_long)persist.version); + ret = EINVAL; + goto err; + } + + if (lp != NULL) { + lp->persist.lg_max = persist.lg_max; + lp->persist.mode = persist.mode; + } + ret = 0; + +err: FREES(p); + return (ret); +} + +/* + * log_close -- + * Close a log. + */ +int +log_close(dblp) + DB_LOG *dblp; +{ + int ret, t_ret; + + ret = 0; + + /* Close the region. */ + if ((t_ret = + __db_rclose(dblp->dbenv, dblp->fd, dblp->maddr)) != 0 && ret == 0) + ret = t_ret; + + /* Close open files, release allocated memory. */ + if (dblp->lfd != -1 && (t_ret = __db_close(dblp->lfd)) != 0 && ret == 0) + ret = t_ret; + if (dblp->c_dbt.data != NULL) + FREE(dblp->c_dbt.data, dblp->c_dbt.ulen); + if (dblp->c_fd != -1 && + (t_ret = __db_close(dblp->c_fd)) != 0 && ret == 0) + ret = t_ret; + + /* Free the structure. */ + if (dblp->dbentry != NULL) + FREE(dblp->dbentry, (dblp->dbentry_cnt * sizeof(DB_ENTRY))); + FREE(dblp, sizeof(DB_LOG)); + + return (ret); +} + +/* + * log_unlink -- + * Exit a log. + */ +int +log_unlink(path, force, dbenv) + const char *path; + int force; + DB_ENV *dbenv; +{ + return (__db_runlink(dbenv, + DB_APP_LOG, path, DB_DEFAULT_LOG_FILE, force)); +} diff --git a/db2/log/log.src b/db2/log/log.src new file mode 100644 index 0000000000..9f4829179b --- /dev/null +++ b/db2/log/log.src @@ -0,0 +1,53 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + * + * @(#)log.src 10.3 (Sleepycat) 8/20/97 + * + * This is the source file used to create the logging functions for the + * log package. Each access method (or set of routines wishing to register + * record types with the transaction system) should have a file like this. + * Each type of log record and its parameters is defined. The basic + * format of a record definition is: + * + * BEGIN <RECORD_TYPE> + * ARG|STRING|POINTER <variable name> <variable type> <printf format> + * ... + * END + * ARG the argument is a simple parameter of the type * specified. + * DBT the argument is a DBT (db.h) containing a length and pointer. + * PTR the argument is a pointer to the data type specified; the entire + * type should be logged. + * + * There are a set of shell scripts of the form xxx.sh that generate c + * code and or h files to process these. (This is probably better done + * in a single PERL script, but for now, this works.) + * + * The DB recovery system requires the following three fields appear in + * every record, and will assign them to the per-record-type structures + * as well as making them the first parameters to the appropriate logging + * call. + * rectype: record-type, identifies the structure and log/read call + * txnid: transaction id, a DBT in this implementation + * prev: the last LSN for this transaction + */ + +/* + * Use the argument of PREFIX as the prefix for all record types, + * routines, id numbers, etc. + */ +PREFIX log + +/* Used for registering new name/id translations. */ +BEGIN register +DBT name DBT s +DBT uid DBT s +ARG id u_int32_t lu +ARG ftype DBTYPE lx +END + +BEGIN unregister +ARG id u_int32_t lu +END diff --git a/db2/log/log_archive.c b/db2/log/log_archive.c new file mode 100644 index 0000000000..d70d4c64c0 --- /dev/null +++ b/db2/log/log_archive.c @@ -0,0 +1,413 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997 + * Sleepycat Software. All rights reserved. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_archive.c 10.23 (Sleepycat) 8/23/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" +#include "db_dispatch.h" +#include "shqueue.h" +#include "log.h" +#include "clib_ext.h" +#include "common_ext.h" + +static int absname __P((char *, char *, char **)); +static int build_data __P((DB_LOG *, char *, char ***, void *(*)(size_t))); +static int cmpfunc __P((const void *, const void *)); +static int usermem __P((char ***, void *(*)(size_t))); + +/* + * log_archive -- + * Supporting function for db_archive(1). + */ +int +log_archive(logp, listp, flags, db_malloc) + DB_LOG *logp; + char ***listp; + int flags; + void *(*db_malloc) __P((size_t)); +{ + DBT rec; + DB_LSN stable_lsn; + u_int32_t fnum; + int array_size, n, ret; + char **array, **arrayp, *name, *p, *pref, buf[MAXPATHLEN]; + + fnum = 0; /* XXX: Shut the compiler up. */ + +#define OKFLAGS (DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG) + if (flags != 0) { + if ((ret = + __db_fchk(logp->dbenv, "log_archive", flags, OKFLAGS)) != 0) + return (ret); + if ((ret = + __db_fcchk(logp->dbenv, + "log_archive", flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0) + return (ret); + } + + /* + * Get the absolute pathname of the current directory. It would + * be nice to get the shortest pathname of the database directory, + * but that's just not possible. + */ + if (LF_ISSET(DB_ARCH_ABS)) { + errno = 0; + if ((pref = getcwd(buf, sizeof(buf))) == NULL) + return (errno == 0 ? ENOMEM : errno); + } else + pref = NULL; + + switch (LF_ISSET(~DB_ARCH_ABS)) { + case DB_ARCH_DATA: + return (build_data(logp, pref, listp, db_malloc)); + case DB_ARCH_LOG: + memset(&rec, 0, sizeof(rec)); + if (F_ISSET(logp, DB_AM_THREAD)) + F_SET(&rec, DB_DBT_MALLOC); + if ((ret = log_get(logp, &stable_lsn, &rec, DB_LAST)) != 0) + return (ret); + if (F_ISSET(logp, DB_AM_THREAD)) + free(rec.data); + fnum = stable_lsn.file; + break; + case 0: + if ((ret = __log_findckp(logp, &stable_lsn)) != 0) { + if (ret != DB_NOTFOUND) + return (ret); + *listp = NULL; + return (0); + } + /* Remove any log files before the last stable LSN. */ + fnum = stable_lsn.file - 1; + break; + } + +#define LIST_INCREMENT 64 + /* Get some initial space. */ + if ((array = + (char **)malloc(sizeof(char *) * (array_size = 10))) == NULL) + return (ENOMEM); + array[0] = NULL; + + /* Build an array of the file names. */ + for (n = 0; fnum > 0; --fnum) { + if ((ret = __log_name(logp->dbenv, fnum, &name)) != 0) + goto err; + if (__db_exists(name, NULL) != 0) + break; + + if (n >= array_size - 1) { + array_size += LIST_INCREMENT; + if ((array = (char **)realloc(array, + sizeof(char *) * array_size)) == NULL) { + ret = ENOMEM; + goto err; + } + } + + if (LF_ISSET(DB_ARCH_ABS)) { + if ((ret = absname(pref, name, &array[n])) != 0) + goto err; + FREES(name); + } else if ((p = __db_rpath(name)) != NULL) { + if ((array[n] = (char *)strdup(p + 1)) == NULL) { + ret = ENOMEM; + goto err; + } + FREES(name); + } else + array[n] = name; + + array[++n] = NULL; + } + + /* If there's nothing to return, we're done. */ + if (n == 0) { + *listp = NULL; + ret = 0; + goto err; + } + + /* Sort the list. */ + qsort(array, (size_t)n, sizeof(char *), cmpfunc); + + /* Rework the memory. */ + if ((ret = usermem(&array, db_malloc)) != 0) + goto err; + + *listp = array; + return (0); + +err: if (array != NULL) { + for (arrayp = array; *arrayp != NULL; ++arrayp) + FREES(*arrayp); + free(array); + } + return (ret); +} + +/* + * build_data -- + * Build a list of datafiles for return. + */ +static int +build_data(logp, pref, listp, db_malloc) + DB_LOG *logp; + char *pref, ***listp; + void *(*db_malloc) __P((size_t)); +{ + DBT rec; + DB_LSN lsn; + __log_register_args *argp; + u_int32_t rectype; + int array_size, last, n, nxt, ret; + char **array, **arrayp, *p, *real_name; + + /* Get some initial space. */ + if ((array = + (char **)malloc(sizeof(char *) * (array_size = 10))) == NULL) + return (ENOMEM); + array[0] = NULL; + + memset(&rec, 0, sizeof(rec)); + if (F_ISSET(logp, DB_AM_THREAD)) + F_SET(&rec, DB_DBT_MALLOC); + for (n = 0, ret = log_get(logp, &lsn, &rec, DB_FIRST); + ret == 0; ret = log_get(logp, &lsn, &rec, DB_NEXT)) { + if (rec.size < sizeof(rectype)) { + ret = EINVAL; + __db_err(logp->dbenv, "log_archive: bad log record"); + goto lg_free; + } + + memcpy(&rectype, rec.data, sizeof(rectype)); + if (rectype != DB_log_register) { + if (F_ISSET(logp, DB_AM_THREAD)) { + free(rec.data); + rec.data = NULL; + } + continue; + } + if ((ret = __log_register_read(rec.data, &argp)) != 0) { + ret = EINVAL; + __db_err(logp->dbenv, + "log_archive: unable to read log record"); + goto lg_free; + } + + if (n >= array_size - 1) { + array_size += LIST_INCREMENT; + if ((array = (char **)realloc(array, + sizeof(char *) * array_size)) == NULL) { + ret = ENOMEM; + goto lg_free; + } + } + + if ((array[n] = (char *)strdup(argp->name.data)) == NULL) { + ret = ENOMEM; +lg_free: if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL) + free(rec.data); + goto err1; + } + + array[++n] = NULL; + free(argp); + + if (F_ISSET(logp, DB_AM_THREAD)) { + free(rec.data); + rec.data = NULL; + } + } + + /* If there's nothing to return, we're done. */ + if (n == 0) { + ret = 0; + *listp = NULL; + goto err1; + } + + /* Sort the list. */ + qsort(array, (size_t)n, sizeof(char *), cmpfunc); + + /* + * Build the real pathnames, discarding nonexistent files and + * duplicates. + */ + for (last = nxt = 0; nxt < n;) { + /* + * Discard duplicates. Last is the next slot we're going + * to return to the user, nxt is the next slot that we're + * going to consider. + */ + if (last != nxt) { + array[last] = array[nxt]; + array[nxt] = NULL; + } + for (++nxt; nxt < n && + strcmp(array[last], array[nxt]) == 0; ++nxt) { + FREES(array[nxt]); + array[nxt] = NULL; + } + + /* Get the real name. */ + if ((ret = __db_appname(logp->dbenv, + DB_APP_DATA, NULL, array[last], NULL, &real_name)) != 0) + goto err2; + + /* If the file doesn't exist, ignore it. */ + if (__db_exists(real_name, NULL) != 0) { + FREES(real_name); + FREES(array[last]); + array[last] = NULL; + continue; + } + + /* Rework the name as requested by the user. */ + FREES(array[last]); + array[last] = NULL; + if (pref != NULL) { + ret = absname(pref, real_name, &array[last]); + FREES(real_name); + if (ret != 0) + goto err2; + } else if ((p = __db_rpath(real_name)) != NULL) { + array[last] = (char *)strdup(p + 1); + FREES(real_name); + if (array[last] == NULL) + goto err2; + } else + array[last] = real_name; + ++last; + } + + /* NULL-terminate the list. */ + array[last] = NULL; + + /* Rework the memory. */ + if ((ret = usermem(&array, db_malloc)) != 0) + goto err1; + + *listp = array; + return (0); + +err2: /* + * XXX + * We've possibly inserted NULLs into the array list, so clean up a + * bit so that the other error processing works. + */ + if (array != NULL) + for (; nxt < n; ++nxt) + FREES(array[nxt]); + /* FALLTHROUGH */ + +err1: if (array != NULL) { + for (arrayp = array; *arrayp != NULL; ++arrayp) + FREES(*arrayp); + free(array); + } + return (ret); +} + +/* + * absname -- + * Return an absolute path name for the file. + */ +static int +absname(pref, name, newnamep) + char *pref, *name, **newnamep; +{ + size_t l_pref, l_name; + char *newname; + + l_pref = strlen(pref); + l_name = strlen(name); + + /* Malloc space for concatenating the two. */ + if ((newname = (char *)malloc(l_pref + l_name + 2)) == NULL) + return (ENOMEM); + + /* Build the name. */ + memcpy(newname, pref, l_pref); + if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL) + newname[l_pref++] = PATH_SEPARATOR[0]; + memcpy(newname + l_pref, name, l_name + 1); + *newnamep = newname; + + return (0); +} + +/* + * usermem -- + * Create a single chunk of memory that holds the returned information. + * If the user has their own malloc routine, use it. + */ +static int +usermem(listp, func) + char ***listp; + void *(*func) __P((size_t)); +{ + size_t len; + char **array, **arrayp, **orig, *strp; + + /* Find out how much space we need. */ + for (len = 0, orig = *listp; *orig != NULL; ++orig) + len += sizeof(char *) + strlen(*orig) + 1; + len += sizeof(char *); + + /* + * Allocate it and set up the pointers. + * + * XXX + * Don't simplify this expression, SunOS compilers don't like it. + */ + if (func == NULL) + array = (char **)malloc(len); + else + array = (char **)func(len); + if (array == NULL) + return (ENOMEM); + strp = (char *)(array + (orig - *listp) + 1); + + /* Copy the original information into the new memory. */ + for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) { + len = strlen(*orig); + memcpy(strp, *orig, len + 1); + *arrayp = strp; + strp += len + 1; + + FREES(*orig); + } + + /* NULL-terminate the list. */ + *arrayp = NULL; + + free(*listp); + *listp = array; + + return (0); +} + +static int +cmpfunc(p1, p2) + const void *p1, *p2; +{ + return (strcmp(*((char **)p1), *((char **)p2))); +} diff --git a/db2/log/log_auto.c b/db2/log/log_auto.c new file mode 100644 index 0000000000..59400087ca --- /dev/null +++ b/db2/log/log_auto.c @@ -0,0 +1,351 @@ +/* Do not edit: automatically built by dist/db_gen.sh. */ +#include "config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <ctype.h> +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "db_page.h" +#include "db_dispatch.h" +#include "log.h" +#include "db_am.h" +#include "common_ext.h" + +/* + * PUBLIC: int __log_register_log + * PUBLIC: __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, + * PUBLIC: DBT *, DBT *, u_int32_t, DBTYPE)); + */ +int __log_register_log(logp, txnid, ret_lsnp, flags, + name, uid, id, ftype) + DB_LOG *logp; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + DBT *name; + DBT *uid; + u_int32_t id; + DBTYPE ftype; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_log_register; + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + null_lsn.file = 0; + null_lsn.offset = 0; + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(u_int32_t) + (name == NULL ? 0 : name->size) + + sizeof(u_int32_t) + (uid == NULL ? 0 : uid->size) + + sizeof(id) + + sizeof(ftype); + if ((logrec.data = (void *)malloc(logrec.size)) == NULL) + return (ENOMEM); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + if (name == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &name->size, sizeof(name->size)); + bp += sizeof(name->size); + memcpy(bp, name->data, name->size); + bp += name->size; + } + if (uid == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &uid->size, sizeof(uid->size)); + bp += sizeof(uid->size); + memcpy(bp, uid->data, uid->size); + bp += uid->size; + } + memcpy(bp, &id, sizeof(id)); + bp += sizeof(id); + memcpy(bp, &ftype, sizeof(ftype)); + bp += sizeof(ftype); +#ifdef DEBUG + if ((u_int32_t)(bp - (u_int8_t *)logrec.data) != logrec.size) + fprintf(stderr, "Error in log record length"); +#endif + ret = __log_put(logp, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + free(logrec.data); + return (ret); +} + +/* + * PUBLIC: int __log_register_print + * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); + */ + +int +__log_register_print(notused1, dbtp, lsnp, notused3, notused4) + DB_LOG *notused1; + DBT *dbtp; + DB_LSN *lsnp; + int notused3; + void *notused4; +{ + __log_register_args *argp; + u_int32_t i; + int c, ret; + + i = 0; + c = 0; + notused1 = NULL; + notused3 = 0; + notused4 = NULL; + + if((ret = __log_register_read(dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]log_register: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tname: "); + for (i = 0; i < argp->name.size; i++) { + c = ((char *)argp->name.data)[i]; + if (isprint(c) || c == 0xa) + putchar(c); + else + printf("%#x ", c); + } + printf("\n"); + printf("\tuid: "); + for (i = 0; i < argp->uid.size; i++) { + c = ((char *)argp->uid.data)[i]; + if (isprint(c) || c == 0xa) + putchar(c); + else + printf("%#x ", c); + } + printf("\n"); + printf("\tid: %lu\n", (u_long)argp->id); + printf("\tftype: 0x%lx\n", (u_long)argp->ftype); + printf("\n"); + free(argp); + return (0); +} + +/* + * PUBLIC: int __log_register_read __P((void *, __log_register_args **)); + */ +int +__log_register_read(recbuf, argpp) + void *recbuf; + __log_register_args **argpp; +{ + __log_register_args *argp; + u_int8_t *bp; + + argp = (__log_register_args *)malloc(sizeof(__log_register_args) + + sizeof(DB_TXN)); + if (argp == NULL) + return (ENOMEM); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->name.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->name.data = bp; + bp += argp->name.size; + memcpy(&argp->uid.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->uid.data = bp; + bp += argp->uid.size; + memcpy(&argp->id, bp, sizeof(argp->id)); + bp += sizeof(argp->id); + memcpy(&argp->ftype, bp, sizeof(argp->ftype)); + bp += sizeof(argp->ftype); + *argpp = argp; + return (0); +} + +/* + * PUBLIC: int __log_unregister_log + * PUBLIC: __P((DB_LOG *, DB_TXN *, DB_LSN *, u_int32_t, + * PUBLIC: u_int32_t)); + */ +int __log_unregister_log(logp, txnid, ret_lsnp, flags, + id) + DB_LOG *logp; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + u_int32_t id; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_log_unregister; + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + null_lsn.file = 0; + null_lsn.offset = 0; + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(id); + if ((logrec.data = (void *)malloc(logrec.size)) == NULL) + return (ENOMEM); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &id, sizeof(id)); + bp += sizeof(id); +#ifdef DEBUG + if ((u_int32_t)(bp - (u_int8_t *)logrec.data) != logrec.size) + fprintf(stderr, "Error in log record length"); +#endif + ret = __log_put(logp, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + free(logrec.data); + return (ret); +} + +/* + * PUBLIC: int __log_unregister_print + * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); + */ + +int +__log_unregister_print(notused1, dbtp, lsnp, notused3, notused4) + DB_LOG *notused1; + DBT *dbtp; + DB_LSN *lsnp; + int notused3; + void *notused4; +{ + __log_unregister_args *argp; + u_int32_t i; + int c, ret; + + i = 0; + c = 0; + notused1 = NULL; + notused3 = 0; + notused4 = NULL; + + if((ret = __log_unregister_read(dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]log_unregister: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tid: %lu\n", (u_long)argp->id); + printf("\n"); + free(argp); + return (0); +} + +/* + * PUBLIC: int __log_unregister_read __P((void *, __log_unregister_args **)); + */ +int +__log_unregister_read(recbuf, argpp) + void *recbuf; + __log_unregister_args **argpp; +{ + __log_unregister_args *argp; + u_int8_t *bp; + + argp = (__log_unregister_args *)malloc(sizeof(__log_unregister_args) + + sizeof(DB_TXN)); + if (argp == NULL) + return (ENOMEM); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->id, bp, sizeof(argp->id)); + bp += sizeof(argp->id); + *argpp = argp; + return (0); +} + +/* + * PUBLIC: int __log_init_print __P((DB_ENV *)); + */ +int +__log_init_print(dbenv) + DB_ENV *dbenv; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, + __log_register_print, DB_log_register)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __log_unregister_print, DB_log_unregister)) != 0) + return (ret); + return (0); +} + +/* + * PUBLIC: int __log_init_recover __P((DB_ENV *)); + */ +int +__log_init_recover(dbenv) + DB_ENV *dbenv; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, + __log_register_recover, DB_log_register)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __log_unregister_recover, DB_log_unregister)) != 0) + return (ret); + return (0); +} + diff --git a/db2/log/log_compare.c b/db2/log/log_compare.c new file mode 100644 index 0000000000..601b25c626 --- /dev/null +++ b/db2/log/log_compare.c @@ -0,0 +1,34 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_compare.c 10.2 (Sleepycat) 6/21/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#endif + +#include "db_int.h" + +/* + * log_compare -- + * Compare two LSN's. + */ +int +log_compare(lsn0, lsn1) + const DB_LSN *lsn0, *lsn1; +{ + if (lsn0->file != lsn1->file) + return (lsn0->file < lsn1->file ? -1 : 1); + + if (lsn0->offset != lsn1->offset) + return (lsn0->offset < lsn1->offset ? -1 : 1); + + return (0); +} diff --git a/db2/log/log_findckp.c b/db2/log/log_findckp.c new file mode 100644 index 0000000000..67fe9c9f50 --- /dev/null +++ b/db2/log/log_findckp.c @@ -0,0 +1,130 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_findckp.c 10.10 (Sleepycat) 7/30/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "log.h" +#include "txn.h" +#include "common_ext.h" + +/* + * __log_findckp -- + * + * Looks for the most recent checkpoint that occurs before the most recent + * checkpoint LSN. This is the point from which recovery can start and the + * point up to which archival/truncation can take place. Checkpoints in + * the log look like: + * + * ------------------------------------------------------------------- + * | ckp A, ckplsn 100 | .... record .... | ckp B, ckplsn 600 | ... + * ------------------------------------------------------------------- + * LSN 500 LSN 1000 + * + * If we read what log returns from using the DB_CKP parameter to logput, + * we'll get the record at LSN 1000. The checkpoint LSN there is 600. + * Now we have to scan backwards looking for a checkpoint before LSN 600. + * We find one at 500. This means that we can truncate the log before + * 500 or run recovery beginning at 500. + * + * Returns 0 if we find a checkpoint. + * Returns errno on error. + * Returns DB_NOTFOUND if we could not find a suitable start point and + * we should start from the beginning. + * + * PUBLIC: int __log_findckp __P((DB_LOG *, DB_LSN *)); + */ +int +__log_findckp(lp, lsnp) + DB_LOG *lp; + DB_LSN *lsnp; +{ + DBT data; + DB_LSN ckp_lsn, last_ckp, next_lsn; + __txn_ckp_args *ckp_args; + int ret, verbose; + + verbose = lp->dbenv != NULL && lp->dbenv->db_verbose != 0; + + /* + * Need to find the appropriate point from which to begin + * recovery. + */ + memset(&data, 0, sizeof(data)); + if (F_ISSET(lp, DB_AM_THREAD)) + F_SET(&data, DB_DBT_MALLOC); + if ((ret = log_get(lp, &last_ckp, &data, DB_CHECKPOINT)) != 0) + return (ret == ENOENT ? DB_NOTFOUND : ret); + ZERO_LSN(ckp_lsn); + + next_lsn = last_ckp; + do { + if (F_ISSET(lp, DB_AM_THREAD)) + free(data.data); + + if ((ret = log_get(lp, &next_lsn, &data, DB_SET)) != 0) + return (ret); + if ((ret = __txn_ckp_read(data.data, &ckp_args)) != 0) { + if (F_ISSET(lp, DB_AM_THREAD)) + free(data.data); + return (ret); + } + if (IS_ZERO_LSN(ckp_lsn)) + ckp_lsn = ckp_args->ckp_lsn; + if (verbose) { + __db_err(lp->dbenv, "Checkpoint at: [%lu][%lu]", + (u_long)last_ckp.file, (u_long)last_ckp.offset); + __db_err(lp->dbenv, "Checkpoint LSN: [%lu][%lu]", + (u_long)ckp_args->ckp_lsn.file, + (u_long)ckp_args->ckp_lsn.offset); + __db_err(lp->dbenv, "Previous checkpoint: [%lu][%lu]", + (u_long)ckp_args->last_ckp.file, + (u_long)ckp_args->last_ckp.offset); + } + last_ckp = next_lsn; + next_lsn = ckp_args->last_ckp; + free(ckp_args); + } while (!IS_ZERO_LSN(next_lsn) && + log_compare(&last_ckp, &ckp_lsn) > 0); + + if (F_ISSET(lp, DB_AM_THREAD)) + free(data.data); + + /* + * At this point, either, next_lsn is ZERO or ckp_lsn is the + * checkpoint lsn and last_ckp is the LSN of the last checkpoint + * before ckp_lsn. If the compare in the loop is still true, then + * next_lsn must be 0 and we need to roll forward from the + * beginning of the log. + */ + if (log_compare(&last_ckp, &ckp_lsn) > 0) { + if ((ret = log_get(lp, &last_ckp, &data, DB_FIRST)) != 0) + return (ret); + if (F_ISSET(lp, DB_AM_THREAD)) + free(data.data); + } + *lsnp = last_ckp; + + if (verbose) + __db_err(lp->dbenv, "Rolling forward from [%lu][%lu]", + (u_long)last_ckp.file, (u_long)last_ckp.offset); + + return (IS_ZERO_LSN(last_ckp) ? DB_NOTFOUND : 0); +} diff --git a/db2/log/log_get.c b/db2/log/log_get.c new file mode 100644 index 0000000000..37eb5cb249 --- /dev/null +++ b/db2/log/log_get.c @@ -0,0 +1,355 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_get.c 10.16 (Sleepycat) 8/19/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "db_page.h" +#include "log.h" +#include "hash.h" +#include "common_ext.h" + +/* + * log_get -- + * Get a log record. + */ +int +log_get(dblp, alsn, dbt, flags) + DB_LOG *dblp; + DB_LSN *alsn; + DBT *dbt; + int flags; +{ + LOG *lp; + int ret; + + /* Validate arguments. */ +#define OKFLAGS (DB_CHECKPOINT | \ + DB_CURRENT | DB_FIRST | DB_LAST | DB_NEXT | DB_PREV | DB_SET) + if (flags != 0) { + if ((ret = + __db_fchk(dblp->dbenv, "log_get", flags, OKFLAGS)) != 0) + return (ret); + switch (flags) { + case DB_CHECKPOINT: + case DB_CURRENT: + case DB_FIRST: + case DB_LAST: + case DB_NEXT: + case DB_PREV: + case DB_SET: + case 0: + break; + default: + return (__db_ferr(dblp->dbenv, "log_get", 1)); + } + } + if (F_ISSET(dblp, DB_AM_THREAD)) { + if (LF_ISSET(DB_NEXT | DB_PREV | DB_CURRENT)) + return (__db_ferr(dblp->dbenv, "log_get", 1)); + if (!F_ISSET(dbt, DB_DBT_USERMEM | DB_DBT_MALLOC)) + return (__db_ferr(dblp->dbenv, "threaded data", 1)); + } + + lp = dblp->lp; + + LOCK_LOGREGION(dblp); + + /* + * If we get one of the log's header records, repeat the operation. + * This assumes that applications don't ever request the log header + * records by LSN, but that seems reasonable to me. + */ + ret = __log_get(dblp, alsn, dbt, flags, 0); + if (ret == 0 && alsn->offset == 0) { + switch (flags) { + case DB_FIRST: + flags = DB_NEXT; + break; + case DB_LAST: + flags = DB_PREV; + break; + } + ret = __log_get(dblp, alsn, dbt, flags, 0); + } + + UNLOCK_LOGREGION(dblp); + + return (ret); +} + +/* + * __log_get -- + * Get a log record; internal version. + * + * PUBLIC: int __log_get __P((DB_LOG *, DB_LSN *, DBT *, int, int)); + */ +int +__log_get(dblp, alsn, dbt, flags, silent) + DB_LOG *dblp; + DB_LSN *alsn; + DBT *dbt; + int flags, silent; +{ + DB_LSN nlsn; + HDR hdr; + LOG *lp; + size_t len; + ssize_t nr; + int cnt, ret; + const char *fail; + char *np, *tbuf; + void *p, *shortp; + + lp = dblp->lp; + fail = np = tbuf = NULL; + + nlsn = dblp->c_lsn; + switch (flags) { + case DB_CHECKPOINT: + nlsn = dblp->lp->c_lsn; + if (IS_ZERO_LSN(nlsn)) { + __db_err(dblp->dbenv, + "log_get: unable to find checkpoint record: no checkpoint set."); + ret = ENOENT; + goto err2; + } + break; + case DB_NEXT: /* Next log record. */ + if (!IS_ZERO_LSN(nlsn)) { + /* Increment the cursor by the cursor record size. */ + nlsn.offset += dblp->c_len; + break; + } + /* FALLTHROUGH */ + case DB_FIRST: /* Find the first log record. */ + /* + * Find any log file. Note, we may have only entered records + * in the buffer, and not yet written a log file. + */ + if ((ret = __log_find(dblp->dbenv, lp, &cnt)) != 0) { + __db_err(dblp->dbenv, + "log_get: unable to find the first record: no log files found."); + goto err2; + } + + /* If there's anything in the buffer, it belongs to file 1. */ + if (cnt == 0) + cnt = 1; + + /* Now go backwards to find the smallest one. */ + for (; cnt > 1; --cnt) + if (__log_valid(dblp->dbenv, NULL, cnt) != 0) { + ++cnt; + break; + } + nlsn.file = cnt; + nlsn.offset = 0; + break; + case DB_CURRENT: /* Current log record. */ + break; + case DB_PREV: /* Previous log record. */ + if (!IS_ZERO_LSN(nlsn)) { + /* If at start-of-file, move to the previous file. */ + if (nlsn.offset == 0) { + if (nlsn.file == 1) + return (DB_NOTFOUND); + + --nlsn.file; + nlsn.offset = dblp->c_off; + } else + nlsn.offset = dblp->c_off; + break; + } + /* FALLTHROUGH */ + case DB_LAST: /* Last log record. */ + nlsn.file = lp->lsn.file; + nlsn.offset = lp->lsn.offset - lp->len; + break; + case DB_SET: /* Set log record. */ + nlsn = *alsn; + break; + } + +retry: + /* Return 1 if the request is past end-of-file. */ + if (nlsn.file > lp->lsn.file || + (nlsn.file == lp->lsn.file && nlsn.offset >= lp->lsn.offset)) + return (DB_NOTFOUND); + + /* If we've switched files, discard the current fd. */ + if (dblp->c_lsn.file != nlsn.file && dblp->c_fd != -1) { + (void)__db_close(dblp->c_fd); + dblp->c_fd = -1; + } + + /* If the entire record is in the in-memory buffer, copy it out. */ + if (nlsn.file == lp->lsn.file && nlsn.offset >= lp->w_off) { + /* Copy the header. */ + p = lp->buf + (nlsn.offset - lp->w_off); + memcpy(&hdr, p, sizeof(HDR)); + + /* Copy the record. */ + len = hdr.len - sizeof(HDR); + if ((ret = __db_retcopy(dbt, (u_int8_t *)p + sizeof(HDR), + len, &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0) + goto err1; + goto cksum; + } + + /* + * Move the file descriptor to the page that has the hdr. We dealt + * with moving to a previous log file in the flags switch code, but + * we don't yet know if we'll need to move to a subsequent file. + * + * Acquire a file descriptor. + */ + if (dblp->c_fd == -1) { + if ((ret = __log_name(dblp->dbenv, nlsn.file, &np)) != 0) + goto err1; + if ((ret = __db_fdopen(np, DB_RDONLY | DB_SEQUENTIAL, + DB_RDONLY | DB_SEQUENTIAL, 0, &dblp->c_fd)) != 0) { + fail = np; + goto err1; + } + free(np); + np = NULL; + } + + /* Seek to the header offset and read the header. */ + if ((ret = __db_lseek(dblp->c_fd, 0, 0, nlsn.offset, SEEK_SET)) != 0) { + fail = "seek"; + goto err1; + } + if ((ret = __db_read(dblp->c_fd, &hdr, sizeof(HDR), &nr)) != 0) { + fail = "read"; + goto err1; + } + if (nr == sizeof(HDR)) + shortp = NULL; + else { + /* If read returns EOF, try the next file. */ + if (nr == 0) { + if (flags != DB_NEXT || nlsn.file == lp->lsn.file) + goto corrupt; + + /* Move to the next file. */ + ++nlsn.file; + nlsn.offset = 0; + goto retry; + } + + /* + * If read returns a short count the rest of the record has + * to be in the in-memory buffer. + */ + if (lp->b_off < sizeof(HDR) - nr) + goto corrupt; + + /* Get the rest of the header from the in-memory buffer. */ + memcpy((u_int8_t *)&hdr + nr, lp->buf, sizeof(HDR) - nr); + shortp = lp->buf + (sizeof(HDR) - nr); + } + + /* + * Check for buffers of 0's, that's what we usually see during + * recovery, although it's certainly not something on which we + * can depend. + */ + if (hdr.len <= sizeof(HDR)) + goto corrupt; + len = hdr.len - sizeof(HDR); + + /* If we've already moved to the in-memory buffer, fill from there. */ + if (shortp != NULL) { + if (lp->b_off < ((u_int8_t *)shortp - lp->buf) + len) + goto corrupt; + if ((ret = __db_retcopy(dbt, shortp, len, + &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0) + goto err1; + goto cksum; + } + + /* Allocate temporary memory to hold the record. */ + if ((tbuf = (char *)malloc(len)) == NULL) { + ret = ENOMEM; + goto err1; + } + + /* + * Read the record into the buffer. If read returns a short count, + * there was an error or the rest of the record is in the in-memory + * buffer. Note, the information may be garbage if we're in recovery, + * so don't read past the end of the buffer's memory. + */ + if ((ret = __db_read(dblp->c_fd, tbuf, len, &nr)) != 0) { + fail = "read"; + goto err1; + } + if (len - nr > sizeof(lp->buf)) + goto corrupt; + if (nr != (ssize_t)len) { + if (lp->b_off < len - nr) + goto corrupt; + + /* Get the rest of the record from the in-memory buffer. */ + memcpy((u_int8_t *)tbuf + nr, lp->buf, len - nr); + } + + /* Copy the record into the user's DBT. */ + if ((ret = __db_retcopy(dbt, tbuf, len, + &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0) + goto err1; + free(tbuf); + +cksum: if (hdr.cksum != __ham_func4(dbt->data, dbt->size)) { + if (!silent) + __db_err(dblp->dbenv, "log_get: checksum mismatch"); + goto corrupt; + } + + /* Update the cursor and the return lsn. */ + dblp->c_off = hdr.prev; + dblp->c_len = hdr.len; + dblp->c_lsn = *alsn = nlsn; + + return (0); + +corrupt:/* + * This is the catchall -- for some reason we didn't find enough + * information or it wasn't reasonable information, and it wasn't + * because a system call failed. + */ + ret = EIO; + fail = "read"; + +err1: if (!silent) + if (fail == NULL) + __db_err(dblp->dbenv, "log_get: %s", strerror(ret)); + else + __db_err(dblp->dbenv, + "log_get: %s: %s", fail, strerror(ret)); +err2: if (np != NULL) + free(np); + if (tbuf != NULL) + free(tbuf); + return (ret); +} diff --git a/db2/log/log_put.c b/db2/log/log_put.c new file mode 100644 index 0000000000..db31f9b0e1 --- /dev/null +++ b/db2/log/log_put.c @@ -0,0 +1,484 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_put.c 10.12 (Sleepycat) 8/20/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "db_page.h" +#include "log.h" +#include "hash.h" +#include "common_ext.h" + +static int __log_fill __P((DB_LOG *, void *, u_int32_t)); +static int __log_newfd __P((DB_LOG *)); +static int __log_write __P((DB_LOG *, void *, u_int32_t)); +static int __log_putr __P((DB_LOG *, const DBT *, u_int32_t)); + +/* + * log_put -- + * Write a log record. + */ +int +log_put(dblp, lsn, dbt, flags) + DB_LOG *dblp; + DB_LSN *lsn; + const DBT *dbt; + int flags; +{ + int ret; + + /* Validate arguments. */ +#define OKFLAGS (DB_CHECKPOINT | DB_FLUSH) + if (flags != 0) { + if ((ret = + __db_fchk(dblp->dbenv, "log_put", flags, OKFLAGS)) != 0) + return (ret); + switch (flags) { + case DB_CHECKPOINT: + case DB_FLUSH: + case 0: + break; + default: + return (__db_ferr(dblp->dbenv, "log_put", 1)); + } + } + + LOCK_LOGREGION(dblp); + + ret = __log_put(dblp, lsn, dbt, flags); + + UNLOCK_LOGREGION(dblp); + + return (ret); +} + +/* + * __log_put -- + * Write a log record; internal version. + * + * PUBLIC: int __log_put __P((DB_LOG *, DB_LSN *, const DBT *, int)); + */ +int +__log_put(dblp, lsn, dbt, flags) + DB_LOG *dblp; + DB_LSN *lsn; + const DBT *dbt; + int flags; +{ + DBT t; + DBT fid_dbt; + DB_LSN r_unused; + FNAME *fnp; + LOG *lp; + u_int32_t lastoff; + int ret; + + lp = dblp->lp; + + /* If this information won't fit in the file, swap files. */ + if (lp->lsn.offset + sizeof(HDR) + dbt->size > lp->persist.lg_max) { + if (sizeof(HDR) + + sizeof(LOGP) + dbt->size > lp->persist.lg_max) { + __db_err(dblp->dbenv, + "log_put: record larger than maximum file size"); + return (EINVAL); + } + if (lp->b_off != 0) { + if ((ret = __log_write(dblp, lp->buf, lp->b_off)) != 0) + return (ret); + if ((ret = __db_fsync(dblp->lfd)) != 0) + return (ret); + lp->s_lsn.file = lp->lsn.file; + lp->s_lsn.offset = lp->lsn.offset - 1; + } + + /* + * Save the last known offset from the previous file, we'll + * need it to initialize the persistent header information. + */ + lastoff = lp->lsn.offset; + + ++lp->lsn.file; + lp->lsn.offset = 0; + lp->w_off = 0; + } else + lastoff = 0; + + /* + * Insert persistent information as the first record in every file. + * Note that the previous length is wrong for the very first record + * of the log, but that's okay, we check for it during retrieval. + */ + if (lp->lsn.offset == 0) { + t.data = &lp->persist; + t.size = sizeof(LOGP); + if ((ret = __log_putr(dblp, + &t, lastoff == 0 ? 0 : lastoff - lp->len)) != 0) + return (ret); + } + + /* Initialize the LSN information returned to the user. */ + lsn->file = lp->lsn.file; + lsn->offset = lp->lsn.offset; + + /* Put out the user's record. */ + if ((ret = __log_putr(dblp, dbt, lp->lsn.offset - lp->len)) != 0) + return (ret); + + /* + * On a checkpoint, we: + * Put out the checkpoint record (above). + * Save the LSN of the checkpoint in the shared region. + * Append the set of file name information into the log. + * Flush the current buffer contents to disk. + * Sync the log to disk. + * Save the time the checkpoint was written. + * Reset the bytes written since the last checkpoint. + */ + if (flags == DB_CHECKPOINT) { + lp->c_lsn = *lsn; + + for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { + t.data = ADDR(dblp, fnp->name_off); + t.size = strlen(t.data) + 1; + memset(&fid_dbt, 0, sizeof(fid_dbt)); + fid_dbt.data = ADDR(dblp, fnp->fileid_off); + fid_dbt.size = DB_FILE_ID_LEN; + if ((ret = __log_register_log(dblp, NULL, &r_unused, + 0, &t, &fid_dbt, fnp->id, fnp->s_type)) != 0) + return (ret); + } + if (lp->b_off != 0 && + (ret = __log_write(dblp, lp->buf, lp->b_off)) != 0) + return (ret); + (void)time(&lp->chkpt); + lp->written = 0; + + if ((ret = __db_fsync(dblp->lfd)) != 0) + return (ret); + lp->s_lsn.file = lp->lsn.file; + lp->s_lsn.offset = lp->lsn.offset - 1; + } + + /* We always flush on a checkpoint. */ + if (flags == DB_FLUSH || flags == DB_CHECKPOINT) { + if (lp->b_off != 0 && + (ret = __log_write(dblp, lp->buf, lp->b_off)) != 0) + return (ret); + + if ((ret = __db_fsync(dblp->lfd)) != 0) + return (ret); + lp->s_lsn.file = lp->lsn.file; + lp->s_lsn.offset = lp->lsn.offset - 1; + } + + /* + * If we just did I/O, i.e., this LSN could have spanned the start of + * the in-core buffer, we remember it so that we can flush correctly + * during a sync. + */ + if (lsn->offset < lp->w_off && lsn->offset + lp->len > lp->w_off) + lp->span_lsn = *lsn; + return (0); +} + +/* + * __log_putr -- + * Actually put a record into the log. + */ +static int +__log_putr(dblp, dbt, prev) + DB_LOG *dblp; + const DBT *dbt; + u_int32_t prev; +{ + HDR hdr; + LOG *lp; + int ret; + + lp = dblp->lp; + + /* + * Initialize the header. If we just switched files, lsn.offset will + * be 0, and what we really want is the offset of the previous record + * in the previous file. Fortunately, prev holds the value we want. + */ + hdr.prev = prev; + hdr.len = sizeof(HDR) + dbt->size; + hdr.cksum = __ham_func4(dbt->data, dbt->size); + + if ((ret = __log_fill(dblp, &hdr, sizeof(HDR))) != 0) + return (ret); + lp->lsn.offset += sizeof(HDR); + + if ((ret = __log_fill(dblp, dbt->data, dbt->size)) != 0) + return (ret); + lp->lsn.offset += dbt->size; + + lp->len = sizeof(HDR) + dbt->size; + return (0); +} + +/* + * log_flush -- + * Write all records less than or equal to the specified LSN. + */ +int +log_flush(dblp, lsn) + DB_LOG *dblp; + const DB_LSN *lsn; +{ + DB_LSN t_lsn; + LOG *lp; + int ret; + + ret = 0; + lp = dblp->lp; + + LOCK_LOGREGION(dblp); + + /* If no LSN specified, flush the entire log. */ + if (lsn == NULL) { + t_lsn.file = lp->lsn.file; + t_lsn.offset = lp->lsn.offset - lp->len; + lsn = &t_lsn; + } + + /* If it's a non-existent record, it's an error. */ + if (lsn->file > lp->lsn.file || + (lsn->file == lp->lsn.file && lsn->offset > lp->lsn.offset)) { + __db_err(dblp->dbenv, "log_flush: LSN past current end-of-log"); + ret = EINVAL; + goto ret1; + } + + /* + * If it's from a previous file, we're done because we sync each + * file when we move to a new one. + */ + if (lsn->file < lp->lsn.file) + goto ret1; + + /* + * If it's less than the last-sync'd offset, we've already sync'd + * this LSN. + */ + if (lsn->offset <= lp->s_lsn.offset) + goto ret1; + + /* + * We may need to write the current buffer. We have to write the + * current buffer if the sync LSN is greater than or equal to the + * saved spanning-LSN. + */ + if (lsn->file >= lp->span_lsn.file && + lsn->offset >= lp->span_lsn.offset) + if ((ret = __log_write(dblp, lp->buf, lp->b_off)) != 0) + goto ret1; + + /* Acquire a file descriptor if we don't have one. */ + if (dblp->lfname != dblp->lp->lsn.file && + (ret = __log_newfd(dblp)) != 0) + goto ret1; + + if ((ret = __db_fsync(dblp->lfd)) != 0) + goto ret1; + + lp->s_lsn.file = lp->lsn.file; + lp->s_lsn.offset = lsn->offset; + +ret1: UNLOCK_LOGREGION(dblp); + return (ret); +} + +/* + * __log_fill -- + * Write information into the log. + */ +static int +__log_fill(dblp, addr, len) + DB_LOG *dblp; + void *addr; + u_int32_t len; +{ + LOG *lp; + u_int32_t nrec; + size_t nw, remain; + int ret; + + /* Copy out the data. */ + for (lp = dblp->lp; len > 0;) { + /* + * If we're on a buffer boundary and the data is big enough, + * copy as many records as we can directly from the data. + */ + if (lp->b_off == 0 && len >= sizeof(lp->buf)) { + nrec = len / sizeof(lp->buf); + if ((ret = __log_write(dblp, + addr, nrec * sizeof(lp->buf))) != 0) + return (ret); + addr = (u_int8_t *)addr + nrec * sizeof(lp->buf); + len -= nrec * sizeof(lp->buf); + continue; + } + + /* Figure out how many bytes we can copy this time. */ + remain = sizeof(lp->buf) - lp->b_off; + nw = remain > len ? len : remain; + memcpy(lp->buf + lp->b_off, addr, nw); + addr = (u_int8_t *)addr + nw; + len -= nw; + lp->b_off += nw; + + /* If we fill the buffer, flush it. */ + if (lp->b_off == sizeof(lp->buf) && + (ret = __log_write(dblp, lp->buf, sizeof(lp->buf))) != 0) + return (ret); + } + return (0); +} + +/* + * __log_write -- + * Write the log buffer to disk. + */ +static int +__log_write(dblp, addr, len) + DB_LOG *dblp; + void *addr; + u_int32_t len; +{ + LOG *lp; + ssize_t nw; + int ret; + + /* + * If we haven't opened the log file yet or the current one + * has changed, acquire a new log file. + */ + lp = dblp->lp; + if (dblp->lfd == -1 || dblp->lfname != lp->lsn.file) + if ((ret = __log_newfd(dblp)) != 0) + return (ret); + + /* + * Seek to the offset in the file (someone may have written it + * since we last did). + */ + if ((ret = __db_lseek(dblp->lfd, 0, 0, lp->w_off, SEEK_SET)) != 0) + return (ret); + if ((ret = __db_write(dblp->lfd, addr, len, &nw)) != 0) + return (ret); + if (nw != (int32_t)len) + return (EIO); + + /* Update the seek offset and reset the buffer offset. */ + lp->b_off = 0; + lp->w_off += len; + lp->written += len; + + return (0); +} + +/* + * log_file -- + * Map a DB_LSN to a file name. + */ +int +log_file(dblp, lsn, namep, len) + DB_LOG *dblp; + const DB_LSN *lsn; + char *namep; + size_t len; +{ + int ret; + char *p; + + LOCK_LOGREGION(dblp); + + ret = __log_name(dblp->dbenv, lsn->file, &p); + + UNLOCK_LOGREGION(dblp); + + if (ret != 0) + return (ret); + + /* Check to make sure there's enough room and copy the name. */ + if (len < strlen(p)) { + *namep = '\0'; + return (ENOMEM); + } + (void)strcpy(namep, p); + free(p); + + return (0); +} + +/* + * __log_newfd -- + * Acquire a file descriptor for the current log file. + */ +static int +__log_newfd(dblp) + DB_LOG *dblp; +{ + int ret; + char *p; + + /* Close any previous file descriptor. */ + if (dblp->lfd != -1) { + (void)__db_close(dblp->lfd); + dblp->lfd = -1; + } + + /* Get the path of the new file and open it. */ + dblp->lfname = dblp->lp->lsn.file; + if ((ret = __log_name(dblp->dbenv, dblp->lfname, &p)) != 0) + return (ret); + if ((ret = __db_fdopen(p, + DB_CREATE | DB_SEQUENTIAL, + DB_CREATE | DB_SEQUENTIAL, + dblp->lp->persist.mode, &dblp->lfd)) != 0) + __db_err(dblp->dbenv, + "log_put: %s: %s", p, strerror(errno)); + FREES(p); + return (ret); +} + +/* + * __log_name -- + * Return the log name for a particular file. + * + * PUBLIC: int __log_name __P((DB_ENV *, int, char **)); + */ +int +__log_name(dbenv, fn, np) + DB_ENV *dbenv; + int fn; + char **np; +{ + char name[sizeof(LFNAME) + 10]; + + (void)snprintf(name, sizeof(name), LFNAME, fn); + return (__db_appname(dbenv, DB_APP_LOG, NULL, name, NULL, np)); +} diff --git a/db2/log/log_rec.c b/db2/log/log_rec.c new file mode 100644 index 0000000000..dbc5960731 --- /dev/null +++ b/db2/log/log_rec.c @@ -0,0 +1,332 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +/* + * Copyright (c) 1995, 1996 + * The President and Fellows of Harvard University. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_rec.c 10.11 (Sleepycat) 8/20/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "log.h" +#include "db_dispatch.h" +#include "common_ext.h" + +static int __log_open_file __P((DB_LOG *, + u_int8_t *, char *, DBTYPE, u_int32_t)); + +/* + * PUBLIC: int __log_register_recover + * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); + */ +int +__log_register_recover(logp, dbtp, lsnp, redo, info) + DB_LOG *logp; + DBT *dbtp; + DB_LSN *lsnp; + int redo; + void *info; +{ + __log_register_args *argp; + int ret; + +#ifdef DEBUG_RECOVER + __log_register_print(logp, dbtp, lsnp, redo, info); +#endif + info = info; /* XXX: Shut the compiler up. */ + lsnp = lsnp; + + F_SET(logp, DB_AM_RECOVER); + + if ((ret = __log_register_read(dbtp->data, &argp)) != 0) + goto out; + + ret = __log_open_file(logp, + argp->uid.data, argp->name.data, argp->ftype, argp->id); + if (ret == ENOENT) { + if (redo == TXN_OPENFILES) + __db_err(logp->dbenv, + "warning: file %s not found", argp->name.data); + ret = 0; + } + +out: F_CLR(logp, DB_AM_RECOVER); + if (argp != NULL) + free(argp); + return (ret); +} + +/* + * PUBLIC: int __log_unregister_recover + * PUBLIC: __P((DB_LOG *, DBT *, DB_LSN *, int, void *)); + */ +int +__log_unregister_recover(logp, dbtp, lsnp, redo, info) + DB_LOG *logp; + DBT *dbtp; + DB_LSN *lsnp; + int redo; + void *info; +{ + __log_unregister_args *argp; + int ret; + +#ifdef DEBUG_RECOVER + __log_unregister_print(logp, dbtp, lsnp, redo, info); +#endif + info = info; /* XXX: Shut the compiler up. */ + lsnp = lsnp; + + if (redo == TXN_OPENFILES || + redo == TXN_BACKWARD_ROLL || redo == TXN_UNDO) + return (0); + + F_SET(logp, DB_AM_RECOVER); + if ((ret = __log_unregister_read(dbtp->data, &argp)) != 0) + goto out; + + LOCK_LOGTHREAD(logp); + if (logp->dbentry[argp->id].dbp == NULL) + ret = EINVAL; + else if (--logp->dbentry[argp->id].refcount == 0) { + ret = logp->dbentry[argp->id].dbp->close( + logp->dbentry[argp->id].dbp, 0); + logp->dbentry[argp->id].dbp = NULL; + } + UNLOCK_LOGTHREAD(logp); + +out: F_CLR(logp, DB_AM_RECOVER); + if (argp != NULL) + free(argp); + return (ret); +} + +/* Hand coded routines. */ + +/* + * Called during log_register recovery. Make sure that we have an + * entry in the dbentry table for this ndx. + * Returns 0 on success, non-zero on error. + */ +static int +__log_open_file(lp, uid, name, ftype, ndx) + DB_LOG *lp; + u_int8_t *uid; + char *name; + DBTYPE ftype; + u_int32_t ndx; +{ + DB *dbp; + int ret; + + LOCK_LOGTHREAD(lp); + if (ndx < lp->dbentry_cnt && + (lp->dbentry[ndx].deleted == 1 || lp->dbentry[ndx].dbp != NULL)) { + lp->dbentry[ndx].refcount++; + + UNLOCK_LOGTHREAD(lp); + return (0); + } + UNLOCK_LOGTHREAD(lp); + + /* Need to open file. */ + dbp = NULL; + if ((ret = db_open(name, ftype, 0, 0, lp->dbenv, NULL, &dbp)) == 0) { + /* + * Verify that we are opening the same file that we were + * referring to when we wrote this log record. + */ + if (memcmp(uid, dbp->lock.fileid, DB_FILE_ID_LEN) != 0) { + (void)dbp->close(dbp, 0); + dbp = NULL; + ret = ENOENT; + } + } + + if (ret == 0 || ret == ENOENT) + (void)__log_add_logid(lp, dbp, ndx); + + return (ret); +} + +/* + * This function returns: + * 0 SUCCESS (the entry was not previously set and is now set or the + * entry was previously set and we just inced the ref count. + * >0 on system error (returns errno value). + * PUBLIC: int __log_add_logid __P((DB_LOG *, DB *, u_int32_t)); + */ +int +__log_add_logid(logp, dbp, ndx) + DB_LOG *logp; + DB *dbp; + u_int32_t ndx; +{ + DB_ENTRY *temp_entryp; + u_int32_t i; + int ret; + + ret = 0; + + LOCK_LOGTHREAD(logp); + /* + * Check if we need to grow the table. + */ + if (logp->dbentry_cnt <= ndx) { + if (logp->dbentry_cnt == 0) { + logp->dbentry = + (DB_ENTRY *)malloc(DB_GROW_SIZE * sizeof(DB_ENTRY)); + if (logp->dbentry == NULL) { + ret = ENOMEM; + goto err; + } + } else { + temp_entryp = (DB_ENTRY *)realloc(logp->dbentry, + (DB_GROW_SIZE + logp->dbentry_cnt) * + sizeof(DB_ENTRY)); + if (temp_entryp == NULL) { + ret = ENOMEM; + goto err; + } + logp->dbentry = temp_entryp; + + } + /* Initialize the new entries. */ + for (i = logp->dbentry_cnt; + i < logp->dbentry_cnt + DB_GROW_SIZE; i++) { + logp->dbentry[i].dbp = NULL; + logp->dbentry[i].deleted = 0; + } + + logp->dbentry_cnt += DB_GROW_SIZE; + } + + if (logp->dbentry[ndx].deleted == 0 && logp->dbentry[ndx].dbp == NULL) { + logp->dbentry[ndx].dbp = dbp; + logp->dbentry[ndx].refcount = 1; + logp->dbentry[ndx].deleted = dbp == NULL; + } else + logp->dbentry[ndx].refcount++; + +err: UNLOCK_LOGTHREAD(logp); + return (ret); +} + + +/* + * __db_fileid_to_db -- + * Return the DB corresponding to the specified fileid. + * + * PUBLIC: int __db_fileid_to_db __P((DB_LOG *, DB **, u_int32_t)); + */ +int +__db_fileid_to_db(logp, dbpp, ndx) + DB_LOG *logp; + DB **dbpp; + u_int32_t ndx; +{ + int ret; + + ret = 0; + LOCK_LOGTHREAD(logp); + + /* + * Return DB_DELETED if the file has been deleted + * (it's not an error). + */ + if (logp->dbentry[ndx].deleted) { + ret = DB_DELETED; + goto err; + } + + /* + * Otherwise return 0, but if we don't have a corresponding DB, + * it's an error. + */ + if ((*dbpp = logp->dbentry[ndx].dbp) == NULL) + ret = ENOENT; + +err: UNLOCK_LOGTHREAD(logp); + return (ret); +} + +/* + * Close files that were opened by the recovery daemon. + * + * PUBLIC: void __log_close_files __P((DB_LOG *)); + */ +void +__log_close_files(logp) + DB_LOG *logp; +{ + u_int32_t i; + + LOCK_LOGTHREAD(logp); + for (i = 0; i < logp->dbentry_cnt; i++) + if (logp->dbentry[i].dbp) + logp->dbentry[i].dbp->close(logp->dbentry[i].dbp, 0); + UNLOCK_LOGTHREAD(logp); +} + +/* + * PUBLIC: void __log_rem_logid __P((DB_LOG *, u_int32_t)); + */ +void +__log_rem_logid(logp, ndx) + DB_LOG *logp; + u_int32_t ndx; +{ + LOCK_LOGTHREAD(logp); + if (--logp->dbentry[ndx].refcount == 0) { + logp->dbentry[ndx].dbp = NULL; + logp->dbentry[ndx].deleted = 0; + } + UNLOCK_LOGTHREAD(logp); +} diff --git a/db2/log/log_register.c b/db2/log/log_register.c new file mode 100644 index 0000000000..582eab9408 --- /dev/null +++ b/db2/log/log_register.c @@ -0,0 +1,199 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997 + * Sleepycat Software. All rights reserved. + */ +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)log_register.c 10.10 (Sleepycat) 8/20/97"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "shqueue.h" +#include "log.h" +#include "common_ext.h" + +/* + * log_register -- + * Register a file name. + */ +int +log_register(dblp, dbp, name, type, idp) + DB_LOG *dblp; + DB *dbp; + const char *name; + DBTYPE type; + u_int32_t *idp; +{ + DBT r_name; + DBT fid_dbt; + DB_LSN r_unused; + FNAME *fnp; + size_t len; + u_int32_t fid; + int inserted, ret; + char *fullname; + void *fidp, *namep; + + fid = 0; + inserted = 0; + fullname = NULL; + fnp = fidp = namep = NULL; + + /* Check the arguments. */ + if (type != DB_BTREE && type != DB_HASH && type != DB_RECNO) { + __db_err(dblp->dbenv, "log_register: unknown DB file type"); + return (EINVAL); + } + + /* Get the log file id. */ + if ((ret = __db_appname(dblp->dbenv, + DB_APP_DATA, NULL, name, NULL, &fullname)) != 0) + return (ret); + + LOCK_LOGREGION(dblp); + + /* + * See if we've already got this file in the log, finding the + * next-to-lowest file id currently in use as we do it. + */ + for (fid = 1, fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { + if (fid <= fnp->id) + fid = fnp->id + 1; + if (!memcmp(dbp->lock.fileid, + ADDR(dblp, fnp->fileid_off), DB_FILE_ID_LEN)) { + ++fnp->ref; + fid = fnp->id; + if (!F_ISSET(dblp, DB_AM_RECOVER) && + (ret = __log_add_logid(dblp, dbp, fid) != 0)) + goto err; + goto ret1; + } + } + + /* Allocate a new file name structure. */ + if ((ret = __db_shalloc(dblp->addr, sizeof(FNAME), 0, &fnp)) != 0) + goto err; + fnp->ref = 1; + fnp->id = fid; + fnp->s_type = type; + + if ((ret = __db_shalloc(dblp->addr, DB_FILE_ID_LEN, 0, &fidp)) != 0) + goto err; + /* + * XXX Now that uids are fixed size, we can put them in the fnp + * structure. + */ + fnp->fileid_off = OFFSET(dblp, fidp); + memcpy(fidp, dbp->lock.fileid, DB_FILE_ID_LEN); + + len = strlen(name) + 1; + if ((ret = __db_shalloc(dblp->addr, len, 0, &namep)) != 0) + goto err; + fnp->name_off = OFFSET(dblp, namep); + memcpy(namep, name, len); + + SH_TAILQ_INSERT_HEAD(&dblp->lp->fq, fnp, q, __fname); + inserted = 1; + + /* Log the registry. */ + if (!F_ISSET(dblp, DB_AM_RECOVER)) { + r_name.data = (void *)name; /* XXX: Yuck! */ + r_name.size = strlen(name) + 1; + memset(&fid_dbt, 0, sizeof(fid_dbt)); + fid_dbt.data = dbp->lock.fileid; + fid_dbt.size = DB_FILE_ID_LEN; + if ((ret = __log_register_log(dblp, NULL, &r_unused, + 0, &r_name, &fid_dbt, fid, type)) != 0) + goto err; + if ((ret = __log_add_logid(dblp, dbp, fid)) != 0) + goto err; + } + + if (0) { +err: /* + * XXX + * We should grow the region. + */ + if (inserted) + SH_TAILQ_REMOVE(&dblp->lp->fq, fnp, q, __fname); + if (namep != NULL) + __db_shalloc_free(dblp->addr, namep); + if (fidp != NULL) + __db_shalloc_free(dblp->addr, fidp); + if (fnp != NULL) + __db_shalloc_free(dblp->addr, fnp); + } + +ret1: UNLOCK_LOGREGION(dblp); + + if (fullname != NULL) + FREES(fullname); + + if (idp != NULL) + *idp = fid; + return (ret); +} + +/* + * log_unregister -- + * Discard a registered file name. + */ +int +log_unregister(dblp, fid) + DB_LOG *dblp; + u_int32_t fid; +{ + DB_LSN r_unused; + FNAME *fnp; + int ret; + + ret = 0; + LOCK_LOGREGION(dblp); + + /* Unlog the registry. */ + if (!F_ISSET(dblp, DB_AM_RECOVER) && + (ret = __log_unregister_log(dblp, NULL, &r_unused, 0, fid)) != 0) + return (ret); + + /* Find the entry in the log. */ + for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname); + fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) + if (fid == fnp->id) + break; + if (fnp == NULL) { + __db_err(dblp->dbenv, "log_unregister: non-existent file id"); + ret = EINVAL; + goto ret1; + } + + /* If more than 1 reference, decrement the reference and return. */ + if (fnp->ref > 1) { + --fnp->ref; + goto ret1; + } + + /* Free the unique file information, name and structure. */ + __db_shalloc_free(dblp->addr, ADDR(dblp, fnp->fileid_off)); + __db_shalloc_free(dblp->addr, ADDR(dblp, fnp->name_off)); + SH_TAILQ_REMOVE(&dblp->lp->fq, fnp, q, __fname); + __db_shalloc_free(dblp->addr, fnp); + + /* Remove from the process local table. */ + __log_rem_logid(dblp, fid); + +ret1: UNLOCK_LOGREGION(dblp); + + return (ret); +} |