📄 log.c
字号:
*q = savech; if (ret != 0) { __db_err(dbenv, "%s: %s", dir, db_strerror(ret)); __os_free(dbenv, p); return (ret); } /* Search for a valid log file name. */ for (cnt = fcnt, clv = logval = 0; --cnt >= 0;) { if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1) != 0) continue; /* * Names of the form log\.[0-9]* are reserved for DB. Other * names sharing LFPREFIX, such as "log.db", are legal. */ for (c = names[cnt] + sizeof(LFPREFIX) - 1; *c != '\0'; c++) if (!isdigit((int)*c)) break; if (*c != '\0') continue; /* * Use atol, not atoi; if an "int" is 16-bits, the largest * log file name won't fit. */ clv = (u_int32_t)atol(names[cnt] + (sizeof(LFPREFIX) - 1)); /* * If searching for the first log file, we want to return the * oldest log file we can read, or, if no readable log files * exist, the newest log file we can't read (the crossover * point between the old and new versions of the log file). * * If we're searching for the last log file, we want to return * the newest log file, period. * * Readable log files should never precede unreadable log * files, that would mean the admin seriously screwed up. */ if (find_first) { if (logval != 0 && status != DB_LV_OLD_UNREADABLE && clv > logval) continue; } else if (logval != 0 && clv < logval) continue; if ((ret = __log_valid(dblp, clv, 1, NULL, 0, &status)) != 0) { __db_err(dbenv, "Invalid log file: %s: %s", names[cnt], db_strerror(ret)); goto err; } switch (status) { case DB_LV_NONEXISTENT: /* __log_valid never returns DB_LV_NONEXISTENT. */ DB_ASSERT(0); break; case DB_LV_INCOMPLETE: /* * The last log file may not have been initialized -- * it's possible to create a log file but not write * anything to it. If performing recovery (that is, * if find_first isn't set), ignore the file, it's * not interesting. If we're searching for the first * log record, return the file (assuming we don't find * something better), as the "real" first log record * is likely to be in the log buffer, and we want to * set the file LSN for our return. */ if (find_first) goto found; break; case DB_LV_OLD_UNREADABLE: /* * If we're searching for the first log file, then we * only want this file if we don't yet have a file or * already have an unreadable file and this one is * newer than that one. If we're searching for the * last log file, we always want this file because we * wouldn't be here if it wasn't newer than our current * choice. */ if (!find_first || logval == 0 || (status == DB_LV_OLD_UNREADABLE && clv > logval)) goto found; break; case DB_LV_NORMAL: case DB_LV_OLD_READABLE:found: logval = clv; logval_status = status; break; } } *valp = logval;err: __os_dirfree(dbenv, names, fcnt); __os_free(dbenv, p); *statusp = logval_status; return (ret);}/* * log_valid -- * Validate a log file. Returns an error code in the event of * a fatal flaw in a the specified log file; returns success with * a code indicating the currentness and completeness of the specified * log file if it is not unexpectedly flawed (that is, if it's perfectly * normal, if it's zero-length, or if it's an old version). * * PUBLIC: int __log_valid __P((DB_LOG *, u_int32_t, int, * PUBLIC: DB_FH **, u_int32_t, logfile_validity *)); */int__log_valid(dblp, number, set_persist, fhpp, flags, statusp) DB_LOG *dblp; u_int32_t number; int set_persist; DB_FH **fhpp; u_int32_t flags; logfile_validity *statusp;{ DB_CIPHER *db_cipher; DB_ENV *dbenv; DB_FH *fhp; HDR *hdr; LOG *lp; LOGP *persist; logfile_validity status; size_t hdrsize, nr, recsize; int is_hmac, ret; u_int8_t *tmp; char *fname; dbenv = dblp->dbenv; db_cipher = dbenv->crypto_handle; fhp = NULL; persist = NULL; status = DB_LV_NORMAL; tmp = NULL; /* Return the file handle to our caller, on request */ if (fhpp != NULL) *fhpp = NULL; if (flags == 0) flags = DB_OSO_RDONLY | DB_OSO_SEQ; /* Try to open the log file. */ if ((ret = __log_name(dblp, number, &fname, &fhp, flags)) != 0) { __os_free(dbenv, fname); return (ret); } 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) goto err; hdr = (HDR *)tmp; persist = (LOGP *)(tmp + hdrsize); /* * Try to read the header. This can fail if the log is truncated, or * if we find a preallocated log file where the header has not yet been * written, so we need to check whether the header is zero-filled. */ if ((ret = __os_read(dbenv, fhp, tmp, recsize + hdrsize, &nr)) != 0 || nr != recsize + hdrsize || (hdr->len == 0 && persist->magic == 0 && persist->log_size == 0)) { 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)); goto err; } /* * 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, "Unacceptable 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; /* This is a non-fatal error, but give some feedback. */ __db_err(dbenv, "Skipping log file %s: historic log version %lu", fname, (u_long)persist->version); /* * 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 headers put 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. * * Override the current log file size. * * 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) { lp = dblp->reginfo.primary; lp->log_size = persist->log_size; lp->persist.mode = persist->mode; }err: if (fname != NULL) __os_free(dbenv, fname); if (ret == 0 && fhpp != NULL) *fhpp = fhp; else /* Must close on error or if we only used it locally. */ (void)__os_closehandle(dbenv, fhp); if (tmp != NULL) __os_free(dbenv, tmp); *statusp = status; return (ret);}/* * __log_dbenv_refresh -- * Clean up after the log system on a close or failed open. * * PUBLIC: int __log_dbenv_refresh __P((DB_ENV *)); */int__log_dbenv_refresh(dbenv) DB_ENV *dbenv;{ DB_LOG *dblp; LOG *lp; REGINFO *reginfo; int ret, t_ret; dblp = dbenv->lg_handle; reginfo = &dblp->reginfo; lp = reginfo->primary; /* We may have opened files as part of XA; if so, close them. */ F_SET(dblp, DBLOG_RECOVER); ret = __dbreg_close_files(dbenv); /* * If a private region, return the memory to the heap. Not needed for * filesystem-backed or system shared memory regions, that memory isn't * owned by any particular process. */ if (F_ISSET(dbenv, DB_ENV_PRIVATE)) { /* Discard the flush mutex. */ __db_shalloc_free(reginfo, R_ADDR(reginfo, lp->flush_mutex_off)); /* Discard the buffer. */ __db_shalloc_free(reginfo, R_ADDR(reginfo, lp->buffer_off)); /* Discard stack of free file IDs. */ if (lp->free_fid_stack != INVALID_ROFF) __db_shalloc_free(reginfo, R_ADDR(reginfo, lp->free_fid_stack)); } /* Discard the per-thread lock. */ if (dblp->mutexp != NULL) __db_mutex_free(dbenv, reginfo, dblp->mutexp); /* Detach from the region. */ if ((t_ret = __db_r_detach(dbenv, reginfo, 0)) != 0 && ret == 0) ret = t_ret; /* Close open files, release allocated memory. */ if (dblp->lfhp != NULL) { if ((t_ret = __os_closehandle(dbenv, dblp->lfhp)) != 0 && ret == 0) ret = t_ret; dblp->lfhp = NULL; } if (dblp->dbentry != NULL) __os_free(dbenv, dblp->dbentry); __os_free(dbenv, dblp); dbenv->lg_handle = NULL; return (ret);}/* * __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;{ /* * This routine is called in two cases: when discarding the mutexes * from a previous Berkeley DB run, during recovery, and two, when * discarding the mutexes as we shut down the database environment. * In the latter case, we also need to discard shared memory segments, * this is the last time we use them, and the last region-specific * call we make. */#ifdef HAVE_MUTEX_SYSTEM_RESOURCES LOG *lp; lp = R_ADDR(infop, infop->rp->primary); /* Destroy mutexes. */ __db_shlocks_destroy(infop, R_ADDR(infop, lp->maint_off)); if (infop->primary != NULL && F_ISSET(dbenv, DB_ENV_PRIVATE)) __db_shalloc_free(infop, R_ADDR(infop, lp->maint_off));#endif if (infop->primary != NULL && F_ISSET(dbenv, DB_ENV_PRIVATE)) __db_shalloc_free(infop, infop->primary);}/* * __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 *, DB_LSN *));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -