📄 log_get.c
字号:
} nlsn.file = lp->lsn.file; nlsn.offset = lp->lsn.offset - lp->len; break; case DB_SET: /* Set log record. */ nlsn = *alsn; break; default: DB_ASSERT(0); ret = EINVAL; goto err; } 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, then either logs are * in-memory, and we're done, or 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; if (lp->db_log_inmemory) goto nohdr; } /* * 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) { /* * 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 LSN %lu/%lu: checksum mismatch", (u_long)nlsn.file, (u_long)nlsn.offset); __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) {nohdr: 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); ret = __db_panic(dbenv, DB_RUNRECOVERY); goto err; 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; int eof; *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. */ if (logc->bp_lsn.file != lsn->file) return (0); if (logc->bp_lsn.offset > lsn->offset) return (0); if (logc->bp_lsn.offset + logc->bp_rlen <= lsn->offset + hdr->size) return (0); /* * Read the record's header and check if the record is entirely held * in the buffer. If the record is not entirely held, get it again. * (The only advantage in having part of the record locally is that * we might avoid a system call because we already have the HDR in * memory.) * * If the header check fails for any reason, it must be because the * LSN is bogus. Fail hard. */ p = logc->bp + (lsn->offset - logc->bp_lsn.offset); memcpy(hdr, p, hdr->size); if (__log_c_hdrchk(logc, lsn, hdr, &eof)) return (DB_NOTFOUND); if (eof || logc->bp_lsn.offset + logc->bp_rlen < lsn->offset + hdr->len) return (0); *pp = p; /* Success. */ return (0);}/* * __log_c_inregion -- * Check to see if the requested record is in the region's buffer. */static int__log_c_inregion(logc, lsn, rlockp, last_lsn, hdr, pp) DB_LOGC *logc; DB_LSN *lsn, *last_lsn; RLOCK *rlockp; HDR *hdr; u_int8_t **pp;{ DB_ENV *dbenv; DB_LOG *dblp; LOG *lp; size_t b_region, len, nr; u_int32_t b_disk; int eof, ret; u_int8_t *p; dbenv = logc->dbenv; dblp = dbenv->lg_handle; lp = ((DB_LOG *)logc->dbenv->lg_handle)->reginfo.primary; ret = 0; b_region = 0; *pp = NULL; /* If we haven't yet acquired the log region lock, do so. */ if (*rlockp == L_NONE) { *rlockp = L_ACQUIRED; R_LOCK(dbenv, &dblp->reginfo); } /* * The routines to read from disk must avoid reading past the logical * end of the log, so pass that information back to it. * * Since they're reading directly from the disk, they must also avoid * reading past the offset we've written out. If the log was * truncated, it's possible that there are zeroes or garbage on * disk after this offset, and the logical end of the log can * come later than this point if the log buffer isn't empty. */ *last_lsn = lp->lsn; if (!lp->db_log_inmemory && last_lsn->offset > lp->w_off) last_lsn->offset = lp->w_off; /* * Test to see if the requested LSN could be part of the region's * buffer. * * During recovery, we read the log files getting the information to * initialize the region. In that case, the region's lsn field will * not yet have been filled in, use only the disk. * * The record must not start at a byte offset after the region buffer's * end, since that means the request is for a record after the end of * the log. Do this test even if the region's buffer is empty -- after * recovery, the log files may continue past the declared end-of-log, * and the disk reading routine will incorrectly attempt to read the * remainder of the log. * * Otherwise, test to see if the region's buffer actually has what we * want: * * The buffer must have some useful content. * The record must be in the same file as the region's buffer and must * start at a byte offset equal to or greater than the region's buffer. */ if (IS_ZERO_LSN(lp->lsn)) return (0); if (log_compare(lsn, &lp->lsn) >= 0) return (DB_NOTFOUND); else if (lp->db_log_inmemory) { if ((ret = __log_inmem_lsnoff(dblp, lsn, &b_region)) != 0) return (ret); } else if (lp->b_off == 0 || log_compare(lsn, &lp->f_lsn) < 0) return (0); /* * The current contents of the cursor's buffer will be useless for a * future call, we're about to overwrite it -- trash it rather than * try and make it look correct. */ logc->bp_rlen = 0; /* * If the requested LSN is greater than the region buffer's first * byte, we know the entire record is in the buffer on a good LSN. * * If we're given a bad LSN, the "entire" record might not be in * our buffer in order to fail at the chksum. __log_c_hdrchk made * sure our dest buffer fits, via bp_maxrec, but we also need to * make sure we don't run off the end of this buffer, the src. * * There is one case where the header check can fail: on a scan through * in-memory logs, when we reach the end of a file we can read an empty * heady. In that case, it's safe to return zero, here: it will be * caught in our caller. Otherwise, the LSN is bogus. Fail hard. */ if (lp->db_log_inmemory || log_compare(lsn, &lp->f_lsn) > 0) { if (!lp->db_log_inmemory) b_region = lsn->offset - lp->w_off; __log_inmem_copyout(dblp, b_region, hdr, hdr->size); if (__log_c_hdrchk(logc, lsn, hdr, &eof) != 0) return (DB_NOTFOUND); if (eof) return (0); if (lp->db_log_inmemory) { if (RINGBUF_LEN(lp, b_region, lp->b_off) < hdr->len) return (DB_NOTFOUND); } else if (lsn->offset + hdr->len > lp->w_off + lp->buffer_size) return (DB_NOTFOUND); if (logc->bp_size <= hdr->len) { len = (size_t)DB_ALIGN(hdr->len * 2, 128); if ((ret = __os_realloc(logc->dbenv, len, &logc->bp)) != 0) return (ret); logc->bp_size = (u_int32_t)len; } __log_inmem_copyout(dblp, b_region, logc->bp, hdr->len); *pp = logc->bp; return (0); } DB_ASSERT(!lp->db_log_inmemory); /* * There's a partial record, that is, the requested record starts * in a log file and finishes in the region buffer. We have to * find out how many bytes of the record are in the region buffer * so we can copy them out into the cursor buffer. First, check * to see if the requested record is the only record in the region * buffer, in which case we should copy the entire region buffer. * * Else, walk back through the region's buffer to find the first LSN * after the record that crosses the buffer boundary -- we can detect * that LSN, because its "prev" field will reference the record we * want. The bytes we need to copy from the region buffer are the * bytes up to the record we find. The bytes we'll need to allocate * to hold the log record are the bytes between the two offsets. */ b_disk = lp->w_off - lsn->offset; if (lp->b_off <= lp->len) b_region = (u_int32_t)lp->b_off; else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -