00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 #include "config.h" 00008 00009 #ifndef lint 00010 static const char revid[] = "$Id: log_8c-source.html,v 1.1 2008年06月08日 10:20:08 sebdiaz Exp $"; 00011 #endif /* not lint */ 00012 00013 #ifndef NO_SYSTEM_INCLUDES 00014 #include <sys/types.h> 00015 00016 #include <errno.h> 00017 #include <stdlib.h> 00018 #include <string.h> 00019 #include <unistd.h> 00020 #endif 00021 00022 #ifdef HAVE_RPC 00023 #include "db_server.h" 00024 #endif 00025 00026 #include "db_int.h" 00027 #include "log.h" 00028 #include "db_dispatch.h" 00029 #include "txn.h" 00030 #include "txn_auto.h" 00031 00032 #ifdef HAVE_RPC 00033 #include "gen_client_ext.h" 00034 #include "rpc_client_ext.h" 00035 #endif 00036 00037 static int __log_init __P((DB_ENV *, DB_LOG *)); 00038 static int __log_recover __P((DB_LOG *)); 00039 00040 /* 00041 * CDB___log_open -- 00042 * Internal version of log_open: only called from DB_ENV->open. 00043 * 00044 * PUBLIC: int CDB___log_open __P((DB_ENV *)); 00045 */ 00046 int 00047 CDB___log_open(dbenv) 00048 DB_ENV *dbenv; 00049 { 00050 DB_LOG *dblp; 00051 LOG *lp; 00052 int ret; 00053 u_int8_t *readbufp; 00054 00055 readbufp = NULL; 00056 00057 /* Create/initialize the DB_LOG structure. */ 00058 if ((ret = CDB___os_calloc(dbenv, 1, sizeof(DB_LOG), &dblp)) != 0) 00059 return (ret); 00060 if ((ret = CDB___os_calloc(dbenv, 1, dbenv->lg_bsize, &readbufp)) != 0) 00061 goto err; 00062 ZERO_LSN(dblp->c_lsn); 00063 dblp->dbenv = dbenv; 00064 00065 /* Join/create the log region. */ 00066 dblp->reginfo.id = REG_ID_LOG; 00067 dblp->reginfo.mode = dbenv->db_mode; 00068 if (F_ISSET(dbenv, DB_ENV_CREATE)) 00069 F_SET(&dblp->reginfo, REGION_CREATE_OK); 00070 if ((ret = CDB___db_r_attach( 00071 dbenv, &dblp->reginfo, LG_BASE_REGION_SIZE + dbenv->lg_bsize)) != 0) 00072 goto err; 00073 00074 dblp->readbufp = readbufp; 00075 00076 /* If we created the region, initialize it. */ 00077 if (F_ISSET(&dblp->reginfo, REGION_CREATE) && 00078 (ret = __log_init(dbenv, dblp)) != 0) 00079 goto err; 00080 00081 /* Set the local addresses. */ 00082 lp = dblp->reginfo.primary = 00083 R_ADDR(&dblp->reginfo, dblp->reginfo.rp->primary); 00084 dblp->bufp = R_ADDR(&dblp->reginfo, lp->buffer_off); 00085 00086 R_UNLOCK(dbenv, &dblp->reginfo); 00087 00088 /* 00089 * If the region is threaded, then we have to lock both the handles 00090 * and the region, and we need to allocate a mutex for that purpose. 00091 */ 00092 if (F_ISSET(dbenv, DB_ENV_THREAD)) { 00093 if ((ret = CDB___db_mutex_alloc( 00094 dbenv, &dblp->reginfo, &dblp->mutexp)) != 0) 00095 goto detach; 00096 if ((ret = __db_mutex_init( 00097 dbenv, dblp->mutexp, 0, MUTEX_THREAD)) != 0) 00098 goto detach; 00099 } 00100 00101 dblp->r_file = 0; 00102 dblp->r_off = 0; 00103 dblp->r_size = 0; 00104 dbenv->lg_handle = dblp; 00105 return (0); 00106 00107 err: if (dblp->reginfo.addr != NULL) { 00108 if (F_ISSET(&dblp->reginfo, REGION_CREATE)) 00109 F_SET(dblp->reginfo.rp, REG_DEAD); 00110 R_UNLOCK(dbenv, &dblp->reginfo); 00111 00112 detach: (void)CDB___db_r_detach(dbenv, &dblp->reginfo, 0); 00113 } 00114 00115 if (readbufp != NULL) 00116 CDB___os_free(readbufp, sizeof(dbenv->lg_bsize)); 00117 CDB___os_free(dblp, sizeof(*dblp)); 00118 return (ret); 00119 } 00120 00121 /* 00122 * __log_init -- 00123 * Initialize a log region in shared memory. 00124 */ 00125 static int 00126 __log_init(dbenv, dblp) 00127 DB_ENV *dbenv; 00128 DB_LOG *dblp; 00129 { 00130 LOG *region; 00131 int ret; 00132 void *p; 00133 00134 if ((ret = CDB___db_shalloc(dblp->reginfo.addr, 00135 sizeof(*region), 0, &dblp->reginfo.primary)) != 0) 00136 goto mem_err; 00137 dblp->reginfo.rp->primary = 00138 R_OFFSET(&dblp->reginfo, dblp->reginfo.primary); 00139 region = dblp->reginfo.primary; 00140 memset(region, 0, sizeof(*region)); 00141 00142 region->persist.lg_max = dbenv->lg_max; 00143 region->persist.magic = DB_LOGMAGIC; 00144 region->persist.version = DB_LOGVERSION; 00145 region->persist.mode = dbenv->db_mode; 00146 SH_TAILQ_INIT(®ion->fq); 00147 00148 /* Initialize LOG LSNs. */ 00149 region->lsn.file = 1; 00150 region->lsn.offset = 0; 00151 00152 /* Initialize the buffer. */ 00153 if ((ret = 00154 CDB___db_shalloc(dblp->reginfo.addr, dbenv->lg_bsize, 0, &p)) != 0) { 00155 mem_err: CDB___db_err(dbenv, "Unable to allocate memory for the log buffer"); 00156 return (ret); 00157 } 00158 region->buffer_size = dbenv->lg_bsize; 00159 region->buffer_off = R_OFFSET(&dblp->reginfo, p); 00160 00161 /* 00162 * XXX: 00163 * Initialize the log file size. This is a hack to push the log's 00164 * maximum size down into the Windows CDB___os_open routine, because it 00165 * wants to pre-allocate it. 00166 */ 00167 dblp->lfh.log_size = dbenv->lg_max; 00168 00169 /* Try and recover any previous log files before releasing the lock. */ 00170 return (__log_recover(dblp)); 00171 } 00172 00173 /* 00174 * __log_recover -- 00175 * Recover a log. 00176 */ 00177 static int 00178 __log_recover(dblp) 00179 DB_LOG *dblp; 00180 { 00181 DBT dbt; 00182 DB_LSN lsn; 00183 LOG *lp; 00184 u_int32_t chk; 00185 int cnt, found_checkpoint, ret; 00186 00187 lp = dblp->reginfo.primary; 00188 00189 /* 00190 * Find a log file. If none exist, we simply return, leaving 00191 * everything initialized to a new log. 00192 */ 00193 if ((ret = CDB___log_find(dblp, 0, &cnt)) != 0) 00194 return (ret); 00195 if (cnt == 0) 00196 return (0); 00197 00198 /* 00199 * We have the last useful log file and we've loaded any persistent 00200 * information. Set the end point of the log past the end of the last 00201 * file. Read the last file, looking for the last checkpoint and 00202 * the log's end. 00203 */ 00204 lp->lsn.file = cnt + 1; 00205 lp->lsn.offset = 0; 00206 lsn.file = cnt; 00207 lsn.offset = 0; 00208 00209 /* Set the cursor. Shouldn't fail, leave error messages on. */ 00210 memset(&dbt, 0, sizeof(dbt)); 00211 if ((ret = CDB___log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0) 00212 return (ret); 00213 00214 /* 00215 * Read to the end of the file, saving checkpoints. This will fail 00216 * at some point, so turn off error messages. 00217 */ 00218 found_checkpoint = 0; 00219 while (CDB___log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) { 00220 if (dbt.size < sizeof(u_int32_t)) 00221 continue; 00222 memcpy(&chk, dbt.data, sizeof(u_int32_t)); 00223 if (chk == DB_txn_ckp) { 00224 lp->chkpt_lsn = lsn; 00225 found_checkpoint = 1; 00226 } 00227 } 00228 00229 /* 00230 * We now know where the end of the log is. Set the first LSN that 00231 * we want to return to an application and the LSN of the last known 00232 * record on disk. 00233 */ 00234 lp->lsn = lp->s_lsn = lsn; 00235 lp->lsn.offset += dblp->c_len; 00236 00237 /* Set up the current buffer information, too. */ 00238 lp->len = dblp->c_len; 00239 lp->b_off = 0; 00240 lp->w_off = lp->lsn.offset; 00241 00242 /* 00243 * It's possible that we didn't find a checkpoint because there wasn't 00244 * one in the last log file. Start searching. 00245 */ 00246 if (!found_checkpoint && cnt > 1) { 00247 lsn.file = cnt; 00248 lsn.offset = 0; 00249 00250 /* Set the cursor. Shouldn't fail, leave error messages on. */ 00251 if ((ret = CDB___log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0) 00252 return (ret); 00253 00254 /* 00255 * Read to the end of the file, saving checkpoints. Again, 00256 * this can fail if there are no checkpoints in any log file, 00257 * so turn error messages off. 00258 */ 00259 while (CDB___log_get(dblp, &lsn, &dbt, DB_PREV, 1) == 0) { 00260 if (dbt.size < sizeof(u_int32_t)) 00261 continue; 00262 memcpy(&chk, dbt.data, sizeof(u_int32_t)); 00263 if (chk == DB_txn_ckp) { 00264 lp->chkpt_lsn = lsn; 00265 found_checkpoint = 1; 00266 break; 00267 } 00268 } 00269 } 00270 00271 /* If we never find a checkpoint, that's okay, just 0 it out. */ 00272 if (!found_checkpoint) 00273 ZERO_LSN(lp->chkpt_lsn); 00274 00275 /* 00276 * Reset the cursor lsn to the beginning of the log, so that an 00277 * initial call to DB_NEXT does the right thing. 00278 */ 00279 ZERO_LSN(dblp->c_lsn); 00280 00281 if (FLD_ISSET(dblp->dbenv->verbose, DB_VERB_RECOVERY)) 00282 CDB___db_err(dblp->dbenv, 00283 "Finding last valid log LSN: file: %lu offset %lu", 00284 (u_long)lp->lsn.file, (u_long)lp->lsn.offset); 00285 00286 return (0); 00287 } 00288 00289 /* 00290 * CDB___log_find -- 00291 * Try to find a log file. If find_first is set, valp will contain 00292 * the number of the first log file, else it will contain the number of 00293 * the last log file. 00294 * 00295 * PUBLIC: int CDB___log_find __P((DB_LOG *, int, int *)); 00296 */ 00297 int 00298 CDB___log_find(dblp, find_first, valp) 00299 DB_LOG *dblp; 00300 int find_first, *valp; 00301 { 00302 u_int32_t clv, logval; 00303 int cnt, fcnt, ret; 00304 const char *dir; 00305 char **names, *p, *q; 00306 00307 /* Return a value of 0 as the log file number on failure. */ 00308 *valp = 0; 00309 00310 /* Find the directory name. */ 00311 if ((ret = CDB___log_name(dblp, 1, &p, NULL, 0)) != 0) 00312 return (ret); 00313 if ((q = CDB___db_rpath(p)) == NULL) 00314 dir = PATH_DOT; 00315 else { 00316 *q = '0円'; 00317 dir = p; 00318 } 00319 00320 /* Get the list of file names. */ 00321 ret = CDB___os_dirlist(dblp->dbenv, dir, &names, &fcnt); 00322 00323 /* 00324 * !!! 00325 * We overwrote a byte in the string with a nul. We have to restore 00326 * the string so that the diagnostic checks in the memory allocation 00327 * code work. 00328 */ 00329 if (q != NULL) 00330 *q = 'a'; 00331 00332 if (ret != 0) { 00333 CDB___db_err(dblp->dbenv, "%s: %s", dir, CDB_db_strerror(ret)); 00334 CDB___os_freestr(p); 00335 return (ret); 00336 } 00337 00338 /* 00339 * Search for a valid log file name. 00340 * 00341 * XXX 00342 * Assumes that atoi(3) returns a 32-bit number. 00343 */ 00344 for (cnt = fcnt, clv = logval = 0; --cnt >= 0;) { 00345 if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1) != 0) 00346 continue; 00347 00348 clv = atoi(names[cnt] + (sizeof(LFPREFIX) - 1)); 00349 if (find_first) { 00350 if (logval != 0 && clv > logval) 00351 continue; 00352 } else 00353 if (logval != 0 && clv < logval) 00354 continue; 00355 00356 if (CDB___log_valid(dblp, clv, 1) == 0) 00357 logval = clv; 00358 } 00359 00360 *valp = logval; 00361 00362 CDB___os_dirfree(names, fcnt); 00363 CDB___os_freestr(p); 00364 00365 return (0); 00366 } 00367 00368 /* 00369 * log_valid -- 00370 * Validate a log file. 00371 * 00372 * PUBLIC: int CDB___log_valid __P((DB_LOG *, u_int32_t, int)); 00373 */ 00374 int 00375 CDB___log_valid(dblp, number, set_persist) 00376 DB_LOG *dblp; 00377 u_int32_t number; 00378 int set_persist; 00379 { 00380 DB_FH fh; 00381 LOG *region; 00382 LOGP persist; 00383 size_t nw; 00384 int ret; 00385 char *fname; 00386 00387 /* Try to open the log file. */ 00388 if ((ret = CDB___log_name(dblp, 00389 number, &fname, &fh, DB_OSO_RDONLY | DB_OSO_SEQ)) != 0) { 00390 CDB___os_freestr(fname); 00391 return (ret); 00392 } 00393 00394 /* Try to read the header. */ 00395 if ((ret = 00396 CDB___os_seek(dblp->dbenv, 00397 &fh, 0, 0, sizeof(HDR), 0, DB_OS_SEEK_SET)) != 0 || 00398 (ret = 00399 CDB___os_read(dblp->dbenv, &fh, &persist, sizeof(LOGP), &nw)) != 0 || 00400 nw != sizeof(LOGP)) { 00401 if (ret == 0) 00402 ret = EIO; 00403 00404 (void)CDB___os_closehandle(&fh); 00405 00406 CDB___db_err(dblp->dbenv, 00407 "Ignoring log file: %s: %s", fname, CDB_db_strerror(ret)); 00408 goto err; 00409 } 00410 (void)CDB___os_closehandle(&fh); 00411 00412 /* Validate the header. */ 00413 if (persist.magic != DB_LOGMAGIC) { 00414 CDB___db_err(dblp->dbenv, 00415 "Ignoring log file: %s: magic number %lx, not %lx", 00416 fname, (u_long)persist.magic, (u_long)DB_LOGMAGIC); 00417 ret = EINVAL; 00418 goto err; 00419 } 00420 if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) { 00421 CDB___db_err(dblp->dbenv, 00422 "Ignoring log file: %s: unsupported log version %lu", 00423 fname, (u_long)persist.version); 00424 ret = EINVAL; 00425 goto err; 00426 } 00427 00428 /* 00429 * If we're going to use this log file, set the region's persistent 00430 * information based on the headers. 00431 */ 00432 if (set_persist) { 00433 region = dblp->reginfo.primary; 00434 region->persist.lg_max = persist.lg_max; 00435 region->persist.mode = persist.mode; 00436 } 00437 ret = 0; 00438 00439 err: CDB___os_freestr(fname); 00440 return (ret); 00441 } 00442 00443 /* 00444 * CDB___log_close -- 00445 * Internal version of log_close: only called from dbenv_refresh. 00446 * 00447 * PUBLIC: int CDB___log_close __P((DB_ENV *)); 00448 */ 00449 int 00450 CDB___log_close(dbenv) 00451 DB_ENV *dbenv; 00452 { 00453 DB_LOG *dblp; 00454 int ret, t_ret; 00455 00456 ret = 0; 00457 dblp = dbenv->lg_handle; 00458 00459 /* We may have opened files as part of XA; if so, close them. */ 00460 F_SET(dblp, DBLOG_RECOVER); 00461 CDB___log_close_files(dbenv); 00462 00463 /* Discard the per-thread lock. */ 00464 if (dblp->mutexp != NULL) 00465 CDB___db_mutex_free(dbenv, &dblp->reginfo, dblp->mutexp); 00466 00467 /* Detach from the region. */ 00468 ret = CDB___db_r_detach(dbenv, &dblp->reginfo, 0); 00469 00470 /* Close open files, release allocated memory. */ 00471 if (F_ISSET(&dblp->lfh, DB_FH_VALID) && 00472 (t_ret = CDB___os_closehandle(&dblp->lfh)) != 0 && ret == 0) 00473 ret = t_ret; 00474 if (dblp->c_dbt.data != NULL) 00475 CDB___os_free(dblp->c_dbt.data, dblp->c_dbt.ulen); 00476 if (F_ISSET(&dblp->c_fh, DB_FH_VALID) && 00477 (t_ret = CDB___os_closehandle(&dblp->c_fh)) != 0 && ret == 0) 00478 ret = t_ret; 00479 if (dblp->dbentry != NULL) 00480 CDB___os_free(dblp->dbentry, 00481 (dblp->dbentry_cnt * sizeof(DB_ENTRY))); 00482 if (dblp->readbufp != NULL) 00483 CDB___os_free(dblp->readbufp, dbenv->lg_bsize); 00484 00485 CDB___os_free(dblp, sizeof(*dblp)); 00486 00487 dbenv->lg_handle = NULL; 00488 return (ret); 00489 } 00490 00491 /* 00492 * CDB_log_stat -- 00493 * Return LOG statistics. 00494 */ 00495 int 00496 CDB_log_stat(dbenv, statp, db_malloc) 00497 DB_ENV *dbenv; 00498 DB_LOG_STAT **statp; 00499 void *(*db_malloc) __P((size_t)); 00500 { 00501 DB_LOG *dblp; 00502 DB_LOG_STAT *stats; 00503 LOG *region; 00504 int ret; 00505 00506 #ifdef HAVE_RPC 00507 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00508 return (__dbcl_log_stat(dbenv, statp, db_malloc)); 00509 #endif 00510 00511 PANIC_CHECK(dbenv); 00512 ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG); 00513 00514 *statp = NULL; 00515 00516 dblp = dbenv->lg_handle; 00517 region = dblp->reginfo.primary; 00518 00519 if ((ret = CDB___os_malloc(dbenv, 00520 sizeof(DB_LOG_STAT), db_malloc, &stats)) != 0) 00521 return (ret); 00522 00523 /* Copy out the global statistics. */ 00524 R_LOCK(dbenv, &dblp->reginfo); 00525 *stats = region->stat; 00526 00527 stats->st_magic = region->persist.magic; 00528 stats->st_version = region->persist.version; 00529 stats->st_mode = region->persist.mode; 00530 stats->st_lg_bsize = region->buffer_size; 00531 stats->st_lg_max = region->persist.lg_max; 00532 00533 stats->st_region_wait = dblp->reginfo.rp->mutex.mutex_set_wait; 00534 stats->st_region_nowait = dblp->reginfo.rp->mutex.mutex_set_nowait; 00535 stats->st_regsize = dblp->reginfo.rp->size; 00536 00537 stats->st_cur_file = region->lsn.file; 00538 stats->st_cur_offset = region->lsn.offset; 00539 00540 R_UNLOCK(dbenv, &dblp->reginfo); 00541 00542 *statp = stats; 00543 return (0); 00544 }