📄 log.c
字号:
} need_free = 0; hdrsize = HDR_NORMAL_SZ; is_hmac = 0; recsize = sizeof(LOGP); if (CRYPTO_ON(dbenv)) { hdrsize = HDR_CRYPTO_SZ; recsize = sizeof(LOGP); recsize += db_cipher->adj_size(recsize); is_hmac = 1; } if ((ret = __os_calloc(dbenv, 1, recsize + hdrsize, &tmp)) != 0) return (ret); need_free = 1; hdr = (HDR *)tmp; persist = (LOGP *)(tmp + hdrsize); /* Try to read the header. */ if ((ret = __os_read(dbenv, &fh, tmp, recsize + hdrsize, &nw)) != 0 || nw != recsize + hdrsize) { if (ret == 0) status = DB_LV_INCOMPLETE; else /* * The error was a fatal read error, not just an * incompletely initialized log file. */ __db_err(dbenv, "Ignoring log file: %s: %s", fname, db_strerror(ret)); (void)__os_closehandle(dbenv, &fh); goto err; } (void)__os_closehandle(dbenv, &fh); /* * Now we have to validate the persistent record. We have * several scenarios we have to deal with: * * 1. User has crypto turned on: * - They're reading an old, unencrypted log file * . We will fail the record size match check below. * - They're reading a current, unencrypted log file * . We will fail the record size match check below. * - They're reading an old, encrypted log file [NOT YET] * . After decryption we'll fail the version check. [NOT YET] * - They're reading a current, encrypted log file * . We should proceed as usual. * 2. User has crypto turned off: * - They're reading an old, unencrypted log file * . We will fail the version check. * - They're reading a current, unencrypted log file * . We should proceed as usual. * - They're reading an old, encrypted log file [NOT YET] * . We'll fail the magic number check (it is encrypted). * - They're reading a current, encrypted log file * . We'll fail the magic number check (it is encrypted). */ if (CRYPTO_ON(dbenv)) { /* * If we are trying to decrypt an unencrypted log * we can only detect that by having an unreasonable * data length for our persistent data. */ if ((hdr->len - hdrsize) != sizeof(LOGP)) { __db_err(dbenv, "log record size mismatch"); goto err; } /* Check the checksum and decrypt. */ if ((ret = __db_check_chksum(dbenv, db_cipher, &hdr->chksum[0], (u_int8_t *)persist, hdr->len - hdrsize, is_hmac)) != 0) { __db_err(dbenv, "log record checksum mismatch"); goto err; } if ((ret = db_cipher->decrypt(dbenv, db_cipher->data, &hdr->iv[0], (u_int8_t *)persist, hdr->len - hdrsize)) != 0) goto err; } /* Validate the header. */ if (persist->magic != DB_LOGMAGIC) { __db_err(dbenv, "Ignoring log file: %s: magic number %lx, not %lx", fname, (u_long)persist->magic, (u_long)DB_LOGMAGIC); ret = EINVAL; goto err; } /* * Set our status code to indicate whether the log file * belongs to an unreadable or readable old version; leave it * alone if and only if the log file version is the current one. */ if (persist->version > DB_LOGVERSION) { /* This is a fatal error--the log file is newer than DB. */ __db_err(dbenv, "Ignoring log file: %s: unsupported log version %lu", fname, (u_long)persist->version); ret = EINVAL; goto err; } else if (persist->version < DB_LOGOLDVER) { status = DB_LV_OLD_UNREADABLE; /* * We don't want to set persistent info based on an * unreadable region, so jump to "err". */ goto err; } else if (persist->version < DB_LOGVERSION) status = DB_LV_OLD_READABLE; /* * Only if we have a current log do we verify the checksum. * We could not check the checksum before checking the magic * and version because old log hdrs have the length and checksum * in a different location. */ if (!CRYPTO_ON(dbenv) && ((ret = __db_check_chksum(dbenv, db_cipher, &hdr->chksum[0], (u_int8_t *)persist, hdr->len - hdrsize, is_hmac)) != 0)) { __db_err(dbenv, "log record checksum mismatch"); goto err; } /* * If the log is readable so far and we're doing system initialization, * set the region's persistent information based on the headers. * * Always set the current log file size. Only set the next log file's * size if the application hasn't set it already. * * XXX * Always use the persistent header's mode, regardless of what was set * in the current environment. We've always done it this way, but it's * probably a bug -- I can't think of a way not-changing the mode would * be a problem, though. */ if (set_persist) { region = dblp->reginfo.primary; region->log_size = persist->log_size; if (region->log_nsize == 0) region->log_nsize = persist->log_size; region->persist.mode = persist->mode; }err: __os_free(dbenv, fname); if (need_free) __os_free(dbenv, tmp); *statusp = status; return (ret);}/* * __log_dbenv_refresh -- * Clean up after the log system on a close or failed open. Called only * from __dbenv_refresh. (Formerly called __log_close.) * * PUBLIC: int __log_dbenv_refresh __P((DB_ENV *)); */int__log_dbenv_refresh(dbenv) DB_ENV *dbenv;{ DB_LOG *dblp; int ret, t_ret; dblp = dbenv->lg_handle; /* We may have opened files as part of XA; if so, close them. */ F_SET(dblp, DBLOG_RECOVER); ret = __dbreg_close_files(dbenv); /* Discard the per-thread lock. */ if (dblp->mutexp != NULL) __db_mutex_free(dbenv, &dblp->reginfo, dblp->mutexp); /* Detach from the region. */ if ((t_ret = __db_r_detach(dbenv, &dblp->reginfo, 0)) != 0 && ret == 0) ret = t_ret; /* Close open files, release allocated memory. */ if (F_ISSET(&dblp->lfh, DB_FH_VALID) && (t_ret = __os_closehandle(dbenv, &dblp->lfh)) != 0 && ret == 0) ret = t_ret; if (dblp->dbentry != NULL) __os_free(dbenv, dblp->dbentry); __os_free(dbenv, dblp); dbenv->lg_handle = NULL; return (ret);}/* * __log_stat -- * Return log statistics. * * PUBLIC: int __log_stat __P((DB_ENV *, DB_LOG_STAT **, u_int32_t)); */int__log_stat(dbenv, statp, flags) DB_ENV *dbenv; DB_LOG_STAT **statp; u_int32_t flags;{ DB_LOG *dblp; DB_LOG_STAT *stats; LOG *region; int ret; PANIC_CHECK(dbenv); ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, "DB_ENV->log_stat", DB_INIT_LOG); *statp = NULL; if ((ret = __db_fchk(dbenv, "DB_ENV->log_stat", flags, DB_STAT_CLEAR)) != 0) return (ret); dblp = dbenv->lg_handle; region = dblp->reginfo.primary; if ((ret = __os_umalloc(dbenv, sizeof(DB_LOG_STAT), &stats)) != 0) return (ret); /* Copy out the global statistics. */ R_LOCK(dbenv, &dblp->reginfo); *stats = region->stat; if (LF_ISSET(DB_STAT_CLEAR)) memset(®ion->stat, 0, sizeof(region->stat)); stats->st_magic = region->persist.magic; stats->st_version = region->persist.version; stats->st_mode = region->persist.mode; stats->st_lg_bsize = region->buffer_size; stats->st_lg_size = region->log_nsize; stats->st_region_wait = dblp->reginfo.rp->mutex.mutex_set_wait; stats->st_region_nowait = dblp->reginfo.rp->mutex.mutex_set_nowait; if (LF_ISSET(DB_STAT_CLEAR)) { dblp->reginfo.rp->mutex.mutex_set_wait = 0; dblp->reginfo.rp->mutex.mutex_set_nowait = 0; } stats->st_regsize = dblp->reginfo.rp->size; stats->st_cur_file = region->lsn.file; stats->st_cur_offset = region->lsn.offset; stats->st_disk_file = region->s_lsn.file; stats->st_disk_offset = region->s_lsn.offset; R_UNLOCK(dbenv, &dblp->reginfo); *statp = stats; return (0);}/* * __log_get_cached_ckp_lsn -- * Retrieve any last checkpoint LSN that we may have found on startup. * * PUBLIC: void __log_get_cached_ckp_lsn __P((DB_ENV *, DB_LSN *)); */void__log_get_cached_ckp_lsn(dbenv, ckp_lsnp) DB_ENV *dbenv; DB_LSN *ckp_lsnp;{ DB_LOG *dblp; LOG *lp; dblp = (DB_LOG *)dbenv->lg_handle; lp = (LOG *)dblp->reginfo.primary; R_LOCK(dbenv, &dblp->reginfo); *ckp_lsnp = lp->cached_ckp_lsn; R_UNLOCK(dbenv, &dblp->reginfo);}/* * __log_region_size -- * Return the amount of space needed for the log region. * Make the region large enough to hold txn_max transaction * detail structures plus some space to hold thread handles * and the beginning of the shalloc region and anything we * need for mutex system resource recording. */static size_t__log_region_size(dbenv) DB_ENV *dbenv;{ size_t s; s = dbenv->lg_regionmax + dbenv->lg_bsize;#ifdef HAVE_MUTEX_SYSTEM_RESOURCES if (F_ISSET(dbenv, DB_ENV_THREAD)) s += sizeof(REGMAINT) + LG_MAINT_SIZE;#endif return (s);}/* * __log_region_destroy * Destroy any region maintenance info. * * PUBLIC: void __log_region_destroy __P((DB_ENV *, REGINFO *)); */void__log_region_destroy(dbenv, infop) DB_ENV *dbenv; REGINFO *infop;{ __db_shlocks_destroy(infop, (REGMAINT *)R_ADDR(infop, ((LOG *)R_ADDR(infop, infop->rp->primary))->maint_off)); COMPQUIET(dbenv, NULL); COMPQUIET(infop, NULL);}/* * __log_vtruncate * This is a virtual truncate. We set up the log indicators to * make everyone believe that the given record is the last one in the * log. Returns with the next valid LSN (i.e., the LSN of the next * record to be written). This is used in replication to discard records * in the log file that do not agree with the master. * * PUBLIC: int __log_vtruncate __P((DB_ENV *, DB_LSN *, DB_LSN *)); */int__log_vtruncate(dbenv, lsn, ckplsn) DB_ENV *dbenv; DB_LSN *lsn, *ckplsn;{ DBT log_dbt; DB_FH fh; DB_LOG *dblp; DB_LOGC *logc; DB_LSN end_lsn; LOG *lp; u_int32_t bytes, c_len; int fn, ret, t_ret; char *fname; /* Need to find out the length of this soon-to-be-last record. */ if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0) return (ret); memset(&log_dbt, 0, sizeof(log_dbt)); ret = logc->get(logc, lsn, &log_dbt, DB_SET); c_len = logc->c_len; if ((t_ret = logc->close(logc, 0)) != 0 && ret == 0) ret = t_ret; if (ret != 0) return (ret); /* Now do the truncate. */ dblp = (DB_LOG *)dbenv->lg_handle; lp = (LOG *)dblp->reginfo.primary; R_LOCK(dbenv, &dblp->reginfo); end_lsn = lp->lsn; lp->lsn = *lsn; lp->len = c_len; lp->lsn.offset += lp->len; /* * I am going to assume that the number of bytes written since * the last checkpoint doesn't exceed a 32-bit number. */ DB_ASSERT(lp->lsn.file >= ckplsn->file); bytes = 0; if (ckplsn->file != lp->lsn.file) { bytes = lp->log_size - ckplsn->offset; if (lp->lsn.file > ckplsn->file + 1) bytes += lp->log_size * (lp->lsn.file - ckplsn->file - 1); bytes += lp->lsn.offset; } else bytes = lp->lsn.offset - ckplsn->offset; lp->stat.st_wc_mbytes += bytes / MEGABYTE; lp->stat.st_wc_bytes += bytes % MEGABYTE; /* * If the saved lsn is greater than our new end of log, reset it * to our current end of log. */ if (log_compare(&lp->s_lsn, lsn) > 0) lp->s_lsn = lp->lsn; /* * If the new end of log is in the middle of the buffer, * don't change the w_off or f_lsn. If the new end is * before the w_off then reset w_off and f_lsn to the new * end of log. */ if (lp->w_off >= lp->lsn.offset) { lp->f_lsn = lp->lsn; lp->w_off = lp->lsn.offset; lp->b_off = 0; } else lp->b_off = lp->lsn.offset - lp->w_off; ZERO_LSN(lp->waiting_lsn); lp->ready_lsn = lp->lsn; lp->wait_recs = 0; lp->rcvd_recs = 0; /* Now throw away any extra log files that we have around. */ for (fn = lp->lsn.file + 1;; fn++) { if (__log_name(dblp, fn, &fname, &fh, DB_OSO_RDONLY) != 0) { __os_free(dbenv, fname); break; } (void)__os_closehandle(dbenv, &fh); ret = __os_unlink(dbenv, fname); __os_free(dbenv, fname); if (ret != 0) goto err; } /* Truncate the log to the new point. */ if ((ret = __log_zero(dbenv, &lp->lsn, &end_lsn)) != 0) goto err;err: R_UNLOCK(dbenv, &dblp->reginfo); return (ret);}/* * __log_is_outdated -- * Used by the replication system to identify if a client's logs * are too old. The log represented by dbenv is compared to the file * number passed in fnum. If the log file fnum does not exist and is * lower-numbered than the current logs, the we return *outdatedp non * zero, else we return it 0. * * PUBLIC: int __log_is_outdated __P((DB_ENV *dbenv, * PUBLIC: u_int32_t fnum, int *outdatedp)); */int__log_is_outdated(dbenv, fnum, outdatedp) DB_ENV *dbenv; u_int32_t fnum; int *outdatedp;{ DB_LOG *dblp; LOG *lp; char *name; int ret; u_int32_t cfile; dblp = dbenv->lg_handle; *outdatedp = 0; if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0) return (ret); /* If the file exists, we're just fine. */ if (__os_exists(name, NULL) == 0) goto out; /* * It didn't exist, decide if the file number is too big or * too little. If it's too little, then we need to indicate * that the LSN is outdated. */ R_LOCK(dbenv, &dblp->reginfo); lp = (LOG *)dblp->reginfo.primary; cfile = lp->lsn.file; R_UNLOCK(dbenv, &dblp->reginfo); if (cfile > fnum) *outdatedp = 1;out: __os_free(dbenv, name); return (ret);}/* * __log_zero -- * Zero out the tail of a log after a truncate. */static int__log_zero(dbenv, from_lsn, to_lsn) DB_ENV *dbenv; DB_LSN *from_lsn, *to_lsn;{ char *lname; DB_LOG *dblp; LOG *lp; int ret; size_t nbytes, len, nw; u_int8_t buf[4096]; u_int32_t mbytes, bytes; dblp = dbenv->lg_handle; lp = (LOG *)dblp->reginfo.primary; lname = NULL; if (dblp->lfname != lp->lsn.file) { if (F_ISSET(&dblp->lfh, DB_FH_VALID)) (void)__os_closehandle(dbenv, &dblp->lfh); dblp->lfname = lp->lsn.file; } if (from_lsn->file != to_lsn->file) { /* We removed some log files; have to 0 to end of file. */ if (!F_ISSET(&dblp->lfh, DB_FH_VALID) && (ret = __log_name(dblp, dblp->lfname, &lname, &dblp->lfh, 0)) != 0) return (ret); if ((ret = __os_ioinfo(dbenv, NULL, &dblp->lfh, &mbytes, &bytes, NULL)) != 0) goto err; len = mbytes * MEGABYTE + bytes - from_lsn->offset; } else if (to_lsn->offset <= from_lsn->offset) return (0); else len = to_lsn->offset = from_lsn->offset; memset(buf, 0, sizeof(buf)); /* Initialize the write position. */ if (!F_ISSET(&dblp->lfh, DB_FH_VALID) && (ret = __log_name(dblp, dblp->lfname, &lname, &dblp->lfh, 0)) != 0) goto err; if ((ret = __os_seek(dbenv, &dblp->lfh, 0, 0, from_lsn->offset, 0, DB_OS_SEEK_SET)) != 0) return (ret); while (len > 0) { nbytes = len > sizeof(buf) ? sizeof(buf) : len; if ((ret = __os_write(dbenv, &dblp->lfh, buf, nbytes, &nw)) != 0) return (ret); len -= nbytes; }err: if (lname != NULL) __os_free(dbenv, lname); return (0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -