📄 log_get.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2002 * Sleepycat Software. All rights reserved. */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: log_get.c,v 11.81 2002/08/14 20:09:27 bostic Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <string.h>#include <unistd.h>#endif#include "db_int.h"#include "dbinc/crypto.h"#include "dbinc/db_page.h"#include "dbinc/hmac.h"#include "dbinc/log.h"#include "dbinc/hash.h"typedef enum { L_ALREADY, L_ACQUIRED, L_NONE } RLOCK;static int __log_c_close __P((DB_LOGC *, u_int32_t));static int __log_c_get __P((DB_LOGC *, DB_LSN *, DBT *, u_int32_t));static int __log_c_get_int __P((DB_LOGC *, DB_LSN *, DBT *, u_int32_t));static int __log_c_hdrchk __P((DB_LOGC *, HDR *, int *));static int __log_c_incursor __P((DB_LOGC *, DB_LSN *, HDR *, u_int8_t **));static int __log_c_inregion __P((DB_LOGC *, DB_LSN *, RLOCK *, DB_LSN *, HDR *, u_int8_t **));static int __log_c_io __P((DB_LOGC *, u_int32_t, u_int32_t, void *, size_t *, int *));static int __log_c_ondisk __P((DB_LOGC *, DB_LSN *, DB_LSN *, int, HDR *, u_int8_t **, int *));static int __log_c_set_maxrec __P((DB_LOGC *, char *));static int __log_c_shortread __P((DB_LOGC *, int));/* * __log_cursor -- * Create a log cursor. * * PUBLIC: int __log_cursor __P((DB_ENV *, DB_LOGC **, u_int32_t)); */int__log_cursor(dbenv, logcp, flags) DB_ENV *dbenv; DB_LOGC **logcp; u_int32_t flags;{ DB_LOGC *logc; int ret; PANIC_CHECK(dbenv); ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, "DB_ENV->log_cursor", DB_INIT_LOG); *logcp = NULL; /* Validate arguments. */ if ((ret = __db_fchk(dbenv, "DB_ENV->log_cursor", flags, 0)) != 0) return (ret); /* Allocate memory for the cursor. */ if ((ret = __os_calloc(dbenv, 1, sizeof(DB_LOGC), &logc)) != 0) goto err; if ((ret = __os_calloc(dbenv, 1, sizeof(DB_FH), &logc->c_fh)) != 0) goto err; logc->bp_size = DB_LOGC_BUF_SIZE; if ((ret = __os_malloc(dbenv, logc->bp_size, &logc->bp)) != 0) goto err; logc->dbenv = dbenv; logc->close = __log_c_close; logc->get = __log_c_get; *logcp = logc; return (0);err: if (logc != NULL) { if (logc->c_fh != NULL) __os_free(dbenv, logc->c_fh); __os_free(dbenv, logc); } return (ret);}/* * __log_c_close -- * Close a log cursor. */static int__log_c_close(logc, flags) DB_LOGC *logc; u_int32_t flags;{ DB_ENV *dbenv; int ret; dbenv = logc->dbenv; PANIC_CHECK(dbenv); if ((ret = __db_fchk(dbenv, "DB_LOGC->close", flags, 0)) != 0) return (ret); if (F_ISSET(logc->c_fh, DB_FH_VALID)) (void)__os_closehandle(dbenv, logc->c_fh); if (logc->c_dbt.data != NULL) __os_free(dbenv, logc->c_dbt.data); __os_free(dbenv, logc->bp); __os_free(dbenv, logc->c_fh); __os_free(dbenv, logc); return (0);}/* * __log_c_get -- * Get a log record. */static int__log_c_get(logc, alsn, dbt, flags) DB_LOGC *logc; DB_LSN *alsn; DBT *dbt; u_int32_t flags;{ DB_ENV *dbenv; DB_LSN saved_lsn; int ret; dbenv = logc->dbenv; PANIC_CHECK(dbenv); /* Validate arguments. */ switch (flags) { case DB_CURRENT: case DB_FIRST: case DB_LAST: case DB_NEXT: case DB_PREV: break; case DB_SET: if (IS_ZERO_LSN(*alsn)) { __db_err(dbenv, "DB_LOGC->get: invalid LSN"); return (EINVAL); } break; default: return (__db_ferr(dbenv, "DB_LOGC->get", 1)); } /* * On error, we take care not to overwrite the caller's LSN. This * is because callers looking for the end of the log loop using the * DB_NEXT flag, and expect to take the last successful lsn out of * the passed-in structure after DB_LOGC->get fails with DB_NOTFOUND. * * !!! * This line is often flagged an uninitialized memory read during a * Purify or similar tool run, as the application didn't initialize * *alsn. If the application isn't setting the DB_SET flag, there is * no reason it should have initialized *alsn, but we can't know that * and we want to make sure we never overwrite whatever the application * put in there. */ saved_lsn = *alsn; /* * If we get one of the log's header records as a result of doing a * DB_FIRST, DB_NEXT, DB_LAST or DB_PREV, repeat the operation, log * file header records aren't useful to applications. */ if ((ret = __log_c_get_int(logc, alsn, dbt, flags)) != 0) { *alsn = saved_lsn; return (ret); } if (alsn->offset == 0 && (flags == DB_FIRST || flags == DB_NEXT || flags == DB_LAST || flags == DB_PREV)) { switch (flags) { case DB_FIRST: flags = DB_NEXT; break; case DB_LAST: flags = DB_PREV; break; } if (F_ISSET(dbt, DB_DBT_MALLOC)) { __os_free(dbenv, dbt->data); dbt->data = NULL; } if ((ret = __log_c_get_int(logc, alsn, dbt, flags)) != 0) { *alsn = saved_lsn; return (ret); } } return (0);}/* * __log_c_get_int -- * Get a log record; internal version. */static int__log_c_get_int(logc, alsn, dbt, flags) DB_LOGC *logc; DB_LSN *alsn; DBT *dbt; u_int32_t flags;{ DB_CIPHER *db_cipher; DB_ENV *dbenv; DB_LOG *dblp; DB_LSN last_lsn, nlsn; HDR hdr; LOG *lp; RLOCK rlock; logfile_validity status; u_int32_t cnt; u_int8_t *rp; int eof, is_hmac, ret; dbenv = logc->dbenv; dblp = dbenv->lg_handle; lp = dblp->reginfo.primary; is_hmac = 0; /* * We don't acquire the log region lock until we need it, and we * release it as soon as we're done. */ rlock = F_ISSET(logc, DB_LOG_LOCKED) ? L_ALREADY : L_NONE; nlsn = logc->c_lsn; switch (flags) { case DB_NEXT: /* Next log record. */ if (!IS_ZERO_LSN(nlsn)) { /* Increment the cursor by the cursor record size. */ nlsn.offset += logc->c_len; break; } flags = DB_FIRST; /* FALLTHROUGH */ case DB_FIRST: /* First log record. */ /* Find the first log file. */ if ((ret = __log_find(dblp, 1, &cnt, &status)) != 0) goto err; /* * DB_LV_INCOMPLETE: * Theoretically, the log file we want could be created * but not yet written, the "first" log record must be * in the log buffer. * DB_LV_NORMAL: * DB_LV_OLD_READABLE: * We found a log file we can read. * DB_LV_NONEXISTENT: * No log files exist, the "first" log record must be in * the log buffer. * DB_LV_OLD_UNREADABLE: * No readable log files exist, we're at the cross-over * point between two versions. The "first" log record * must be in the log buffer. */ switch (status) { case DB_LV_INCOMPLETE: DB_ASSERT(lp->lsn.file == cnt); /* FALLTHROUGH */ case DB_LV_NORMAL: case DB_LV_OLD_READABLE: nlsn.file = cnt; break; case DB_LV_NONEXISTENT: nlsn.file = 1; DB_ASSERT(lp->lsn.file == nlsn.file); break; case DB_LV_OLD_UNREADABLE: nlsn.file = cnt + 1; DB_ASSERT(lp->lsn.file == nlsn.file); break; } 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 || __log_valid(dblp, nlsn.file - 1, 0, &status) != 0) { ret = DB_NOTFOUND; goto err; } if (status != DB_LV_NORMAL && status != DB_LV_OLD_READABLE) { ret = DB_NOTFOUND; goto err; } --nlsn.file; } nlsn.offset = logc->c_prev; break; } /* FALLTHROUGH */ case DB_LAST: /* Last log record. */ if (rlock == L_NONE) { rlock = L_ACQUIRED; R_LOCK(dbenv, &dblp->reginfo); } nlsn.file = lp->lsn.file; nlsn.offset = lp->lsn.offset - lp->len; break; case DB_SET: /* Set log record. */ nlsn = *alsn; break; } if (0) { /* Move to the next file. */next_file: ++nlsn.file; nlsn.offset = 0; } /* * The above switch statement should have set nlsn to the lsn of * the requested record. */ if (CRYPTO_ON(dbenv)) { hdr.size = HDR_CRYPTO_SZ; is_hmac = 1; } else { hdr.size = HDR_NORMAL_SZ; is_hmac = 0; } /* Check to see if the record is in the cursor's buffer. */ if ((ret = __log_c_incursor(logc, &nlsn, &hdr, &rp)) != 0) goto err; if (rp != NULL) goto cksum; /* * Look to see if we're moving backward in the log with the last record * coming from the disk -- it means the record can't be in the region's * buffer. Else, check the region's buffer. * * If the record isn't in the region's buffer, we're going to have to * read the record from disk. We want to make a point of not reading * past the end of the logical log (after recovery, there may be data * after the end of the logical log, not to mention the log file may * have been pre-allocated). So, zero out last_lsn, and initialize it * inside __log_c_inregion -- if it's still zero when we check it in * __log_c_ondisk, that's OK, it just means the logical end of the log * isn't an issue for this request. */ ZERO_LSN(last_lsn); if (!F_ISSET(logc, DB_LOG_DISK) || log_compare(&nlsn, &logc->c_lsn) > 0) { F_CLR(logc, DB_LOG_DISK); if ((ret = __log_c_inregion(logc, &nlsn, &rlock, &last_lsn, &hdr, &rp)) != 0) goto err; if (rp != NULL) goto cksum; } /* * We have to read from an on-disk file to retrieve the record. * If we ever can't retrieve the record at offset 0, we're done, * return EOF/DB_NOTFOUND. * * Discard the region lock if we're still holding it, the on-disk * reading routines don't need it. */ if (rlock == L_ACQUIRED) { rlock = L_NONE; R_UNLOCK(dbenv, &dblp->reginfo); } if ((ret = __log_c_ondisk( logc, &nlsn, &last_lsn, flags, &hdr, &rp, &eof)) != 0) goto err; if (eof == 1) { /* * Only DB_NEXT automatically moves to the next file, and * it only happens once. */ if (flags != DB_NEXT || nlsn.offset == 0) return (DB_NOTFOUND); goto next_file; } F_SET(logc, DB_LOG_DISK);cksum: /* * Discard the region lock if we're still holding it. (The path to * get here is that we acquired the lock because of the caller's * flag argument, but we found the record in the cursor's buffer. * Improbable, but it's easy to avoid. */ if (rlock == L_ACQUIRED) { rlock = L_NONE; R_UNLOCK(dbenv, &dblp->reginfo); } /* * Checksum: there are two types of errors -- a configuration error * or a checksum mismatch. The former is always bad. The latter is * OK if we're searching for the end of the log, and very, very bad * if we're reading random log records. */ db_cipher = dbenv->crypto_handle; if ((ret = __db_check_chksum(dbenv, db_cipher, hdr.chksum, rp + hdr.size, hdr.len - hdr.size, is_hmac)) != 0) { if (F_ISSET(logc, DB_LOG_SILENT_ERR)) { if (ret == 0 || ret == -1) ret = EIO; } else if (ret == -1) { __db_err(dbenv, "DB_LOGC->get: log record checksum mismatch"); __db_err(dbenv, "DB_LOGC->get: catastrophic recovery may be required"); ret = __db_panic(dbenv, DB_RUNRECOVERY); } goto err; } /* * If we got a 0-length record, that means we're in the midst of * some bytes that got 0'd as the result of a vtruncate. We're * going to have to retry. */ if (hdr.len == 0) { switch (flags) { case DB_FIRST: case DB_NEXT: /* Zero'd records always indicate the end of a file. */ goto next_file; case DB_LAST: case DB_PREV: /* * We should never get here. If we recover a log * file with 0's at the end, we'll treat the 0'd * headers as the end of log and ignore them. If * we're reading backwards from another file, then * the first record in that new file should have its * prev field set correctly. */ __db_err(dbenv, "Encountered zero length records while traversing backwards"); DB_ASSERT(0); case DB_SET: default: /* Return the 0-length record. */ break; } } /* Copy the record into the user's DBT. */ if ((ret = __db_retcopy(dbenv, dbt, rp + hdr.size, (u_int32_t)(hdr.len - hdr.size), &logc->c_dbt.data, &logc->c_dbt.ulen)) != 0) goto err; if (CRYPTO_ON(dbenv)) { if ((ret = db_cipher->decrypt(dbenv, db_cipher->data, hdr.iv, dbt->data, hdr.len - hdr.size)) != 0) { ret = EAGAIN; goto err; } /* * Return the original log record size to the user, * even though we've allocated more than that, possibly. * The log record is decrypted in the user dbt, not in * the buffer, so we must do this here after decryption, * not adjust the len passed to the __db_retcopy call. */ dbt->size = hdr.orig_size; } /* Update the cursor and the returned LSN. */ *alsn = nlsn; logc->c_lsn = nlsn; logc->c_len = hdr.len; logc->c_prev = hdr.prev;err: if (rlock == L_ACQUIRED) R_UNLOCK(dbenv, &dblp->reginfo); return (ret);}/* * __log_c_incursor -- * Check to see if the requested record is in the cursor's buffer. */static int__log_c_incursor(logc, lsn, hdr, pp) DB_LOGC *logc; DB_LSN *lsn; HDR *hdr; u_int8_t **pp;{ u_int8_t *p; *pp = NULL; /* * Test to see if the requested LSN could be part of the cursor's * buffer. * * The record must be part of the same file as the cursor's buffer. * The record must start at a byte offset equal to or greater than * the cursor buffer. * The record must not start at a byte offset after the cursor * buffer's end. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -