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 /* 00008 * Copyright (c) 1995, 1996 00009 * The President and Fellows of Harvard University. All rights reserved. 00010 * 00011 * Redistribution and use in source and binary forms, with or without 00012 * modification, are permitted provided that the following conditions 00013 * are met: 00014 * 1. Redistributions of source code must retain the above copyright 00015 * notice, this list of conditions and the following disclaimer. 00016 * 2. Redistributions in binary form must reproduce the above copyright 00017 * notice, this list of conditions and the following disclaimer in the 00018 * documentation and/or other materials provided with the distribution. 00019 * 3. Neither the name of the University nor the names of its contributors 00020 * may be used to endorse or promote products derived from this software 00021 * without specific prior written permission. 00022 * 00023 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00024 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00027 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00028 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00029 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00032 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00033 * SUCH DAMAGE. 00034 */ 00035 00036 #include "config.h" 00037 00038 #ifndef lint 00039 static const char revid[] = "$Id: log__rec_8c-source.html,v 1.1 2008年06月08日 10:20:28 sebdiaz Exp $"; 00040 #endif /* not lint */ 00041 00042 #ifndef NO_SYSTEM_INCLUDES 00043 #include <sys/types.h> 00044 00045 #include <errno.h> 00046 #include <string.h> 00047 #endif 00048 00049 #include "db_int.h" 00050 #include "db_page.h" 00051 #include "db_ext.h" 00052 #include "log.h" 00053 00054 static int __log_do_open __P((DB_ENV *, DB_LOG *, 00055 u_int8_t *, char *, DBTYPE, int32_t, db_pgno_t)); 00056 static int __log_lid_to_fname __P((DB_LOG *, int32_t, FNAME **)); 00057 static int __log_open_file __P((DB_ENV *, DB_LOG *, __log_register_args *)); 00058 00059 /* 00060 * PUBLIC: int CDB___log_register_recover 00061 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00062 */ 00063 int 00064 CDB___log_register_recover(dbenv, dbtp, lsnp, op, info) 00065 DB_ENV *dbenv; 00066 DBT *dbtp; 00067 DB_LSN *lsnp; 00068 db_recops op; 00069 void *info; 00070 { 00071 DB_ENTRY *dbe; 00072 DB_LOG *logp; 00073 DB *dbp; 00074 __log_register_args *argp; 00075 int do_rem, ret, t_ret; 00076 00077 logp = dbenv->lg_handle; 00078 dbp = NULL; 00079 00080 #ifdef DEBUG_RECOVER 00081 CDB___log_register_print(logp, dbtp, lsnp, op, info); 00082 #endif 00083 COMPQUIET(lsnp, NULL); 00084 00085 if ((ret = CDB___log_register_read(dbenv, dbtp->data, &argp)) != 0) 00086 goto out; 00087 00088 if ((argp->opcode == LOG_OPEN && 00089 (DB_REDO(op) || op == DB_TXN_OPENFILES)) || 00090 (argp->opcode == LOG_CLOSE && DB_UNDO(op))) { 00091 /* 00092 * If we are redoing an open or undoing a close, then we need 00093 * to open a file. We must open the file even if 00094 * the meta page is not yet written as we may be creating it. 00095 */ 00096 if (op == DB_TXN_OPENFILES) 00097 F_SET(logp, DBLOG_FORCE_OPEN); 00098 ret = __log_open_file(dbenv, logp, argp); 00099 F_CLR(logp, DBLOG_FORCE_OPEN); 00100 if (ret == ENOENT || ret == EINVAL) { 00101 if (op == DB_TXN_OPENFILES && argp->name.size != 0 && 00102 (ret = CDB___db_txnlist_delete(dbenv, info, 00103 argp->name.data, argp->fileid, 0)) != 0) 00104 goto out; 00105 ret = 0; 00106 } 00107 } else if (argp->opcode != LOG_CHECKPOINT) { 00108 /* 00109 * If we are undoing an open, then we need to close the file. 00110 * 00111 * If the file is deleted, then we can just ignore this close. 00112 * Otherwise, we should usually have a valid dbp we should 00113 * close or whose reference count should be decremented. 00114 * However, if we shut down without closing a file, we may, in 00115 * fact, not have the file open, and that's OK. 00116 */ 00117 do_rem = 0; 00118 MUTEX_THREAD_LOCK(logp->mutexp); 00119 if (argp->fileid < logp->dbentry_cnt) { 00120 dbe = &logp->dbentry[argp->fileid]; 00121 if (dbe->refcount == 1) { 00122 ret = CDB___db_txnlist_close(info, 00123 argp->fileid, dbe->count); 00124 if ((dbp = TAILQ_FIRST(&dbe->dblist)) != NULL) 00125 (void) CDB_log_unregister(dbenv, dbp); 00126 do_rem = 1; 00127 } else 00128 dbe->refcount--; 00129 } 00130 MUTEX_THREAD_UNLOCK(logp->mutexp); 00131 if (do_rem) { 00132 (void)CDB___log_rem_logid(logp, dbp, argp->fileid); 00133 /* 00134 * If remove or rename has closed the file, don't 00135 * sync. 00136 */ 00137 if (dbp != NULL && 00138 (t_ret = dbp->close(dbp, 00139 dbp->mpf == NULL ? DB_NOSYNC : 0)) != 0 && ret == 0) 00140 ret = t_ret; 00141 } 00142 } else if (DB_UNDO(op) || op == DB_TXN_OPENFILES) { 00143 /* 00144 * It's a checkpoint and we are rolling backward. It 00145 * is possible that the system was shut down and thus 00146 * ended with a stable checkpoint; this file was never 00147 * closed and has therefore not been reopened yet. If 00148 * so, we need to try to open it. 00149 */ 00150 ret = __log_open_file(dbenv, logp, argp); 00151 if (ret == ENOENT || ret == EINVAL) { 00152 if (argp->name.size != 0 && (ret = 00153 CDB___db_txnlist_delete(dbenv, info, 00154 argp->name.data, argp->fileid, 0)) != 0) 00155 goto out; 00156 ret = 0; 00157 } 00158 } 00159 00160 out: if (argp != NULL) 00161 CDB___os_free(argp, 0); 00162 return (ret); 00163 } 00164 00165 /* 00166 * __log_open_file -- 00167 * Called during CDB_log_register recovery. Make sure that we have an 00168 * entry in the dbentry table for this ndx. Returns 0 on success, 00169 * non-zero on error. 00170 */ 00171 static int 00172 __log_open_file(dbenv, lp, argp) 00173 DB_ENV *dbenv; 00174 DB_LOG *lp; 00175 __log_register_args *argp; 00176 { 00177 DB_ENTRY *dbe; 00178 DB *dbp; 00179 00180 /* 00181 * We never re-open temporary files. Temp files are only 00182 * useful during aborts in which case the dbp was entered 00183 * when the file was registered. During recovery, we treat 00184 * temp files as properly deleted files, allowing the open to 00185 * fail and not reporting any errors when recovery fails to 00186 * get a valid dbp from db_fileid_to_db. 00187 */ 00188 if (argp->name.size == 0) { 00189 (void)CDB___log_add_logid(dbenv, lp, NULL, argp->fileid); 00190 return (ENOENT); 00191 } 00192 00193 /* 00194 * Because of reference counting, we cannot automatically close files 00195 * during recovery, so when we're opening, we have to check that the 00196 * name we are opening is what we expect. If it's not, then we close 00197 * the old file and open the new one. 00198 */ 00199 MUTEX_THREAD_LOCK(lp->mutexp); 00200 if (argp->fileid < lp->dbentry_cnt) 00201 dbe = &lp->dbentry[argp->fileid]; 00202 else 00203 dbe = NULL; 00204 00205 if (dbe != NULL) { 00206 dbe->deleted = 0; 00207 if ((dbp = TAILQ_FIRST(&dbe->dblist)) != NULL) { 00208 if (dbp->meta_pgno != argp->meta_pgno || 00209 memcmp(dbp->fileid, 00210 argp->uid.data, DB_FILE_ID_LEN) != 0) { 00211 MUTEX_THREAD_UNLOCK(lp->mutexp); 00212 goto reopen; 00213 } 00214 if (!F_ISSET(lp, DBLOG_RECOVER)) 00215 dbe->refcount++; 00216 MUTEX_THREAD_UNLOCK(lp->mutexp); 00217 return (0); 00218 } 00219 } 00220 00221 MUTEX_THREAD_UNLOCK(lp->mutexp); 00222 if (0) { 00223 reopen: (void) CDB_log_unregister(dbp->dbenv, dbp); 00224 (void) CDB___log_rem_logid(lp, dbp, argp->fileid); 00225 dbp->close(dbp, 0); 00226 } 00227 00228 return (__log_do_open(dbenv, lp, 00229 argp->uid.data, argp->name.data, 00230 argp->ftype, argp->fileid, argp->meta_pgno)); 00231 } 00232 00233 /* 00234 * log_reopen_file -- close and reopen a db file. 00235 * Must be called when a metadata page changes. 00236 * 00237 * PUBLIC: int CDB___log_reopen_file __P((DB_ENV *, 00238 * PUBLIC: char *, int32_t, u_int8_t *, db_pgno_t)); 00239 * 00240 */ 00241 int 00242 CDB___log_reopen_file(dbenv, name, ndx, fileid, meta_pgno) 00243 DB_ENV *dbenv; 00244 char *name; 00245 int32_t ndx; 00246 u_int8_t *fileid; 00247 db_pgno_t meta_pgno; 00248 { 00249 DB *dbp; 00250 DB_LOG *logp; 00251 DBTYPE ftype; 00252 int ret; 00253 00254 logp = dbenv->lg_handle; 00255 00256 if ((ret = CDB___db_fileid_to_db(dbenv, &dbp, ndx, 0)) != 0) 00257 goto out; 00258 ftype = dbp->type; 00259 (void) CDB_log_unregister(dbenv, dbp); 00260 (void) CDB___log_rem_logid(logp, dbp, ndx); 00261 (void) dbp->close(dbp, 0); 00262 00263 ret = __log_do_open(dbenv, logp, fileid, name, ftype, ndx, meta_pgno); 00264 00265 out: return (ret); 00266 } 00267 00268 /* 00269 * __log_do_open -- 00270 * Open files referenced in the log. This is the part of the open that 00271 * is not protected by the thread mutex. 00272 */ 00273 static int 00274 __log_do_open(dbenv, lp, uid, name, ftype, ndx, meta_pgno) 00275 DB_ENV *dbenv; 00276 DB_LOG *lp; 00277 u_int8_t *uid; 00278 char *name; 00279 DBTYPE ftype; 00280 int32_t ndx; 00281 db_pgno_t meta_pgno; 00282 { 00283 DB *dbp; 00284 int ret; 00285 u_int8_t zeroid[DB_FILE_ID_LEN]; 00286 00287 if ((ret = CDB_db_create(&dbp, lp->dbenv, 0)) != 0) 00288 return (ret); 00289 00290 dbp->log_fileid = ndx; 00291 00292 /* 00293 * This is needed to signal to the locking routines called while 00294 * opening databases that we are potentially undoing a transaction 00295 * from an XA process. Since the XA process does not share 00296 * locks with the aborting transaction this prevents us from 00297 * deadlocking during the open during rollback. 00298 * Because this routine is called either during recovery or during an 00299 * XA_ABORT, we can safely set DB_AM_RECOVER in the dbp since it 00300 * will not be shared with other threads. 00301 */ 00302 F_SET(dbp, DB_AM_RECOVER); 00303 if (meta_pgno != PGNO_BASE_MD) 00304 memcpy(dbp->fileid, uid, DB_FILE_ID_LEN); 00305 dbp->type = ftype; 00306 if ((ret = CDB___db_dbopen(dbp, name, 0, 0600, meta_pgno)) == 0) { 00307 /* 00308 * Verify that we are opening the same file that we were 00309 * referring to when we wrote this log record. 00310 */ 00311 if (memcmp(uid, dbp->fileid, DB_FILE_ID_LEN) != 0) { 00312 memset(zeroid, 0, DB_FILE_ID_LEN); 00313 if (memcmp(dbp->fileid, zeroid, DB_FILE_ID_LEN) != 0) 00314 goto not_right; 00315 memcpy(dbp->fileid, uid, DB_FILE_ID_LEN); 00316 } 00317 if (IS_RECOVERING(dbenv)) { 00318 (void)CDB_log_register(dbp->dbenv, dbp, name); 00319 (void)CDB___log_add_logid(dbenv, lp, dbp, ndx); 00320 } 00321 return (0); 00322 } 00323 00324 not_right: 00325 (void)dbp->close(dbp, 0); 00326 (void)CDB___log_add_logid(dbenv, lp, NULL, ndx); 00327 00328 return (ENOENT); 00329 } 00330 00331 /* 00332 * CDB___log_add_logid -- 00333 * Adds a DB entry to the log's DB entry table. 00334 * 00335 * PUBLIC: int CDB___log_add_logid __P((DB_ENV *, DB_LOG *, DB *, int32_t)); 00336 */ 00337 int 00338 CDB___log_add_logid(dbenv, logp, dbp, ndx) 00339 DB_ENV *dbenv; 00340 DB_LOG *logp; 00341 DB *dbp; 00342 int32_t ndx; 00343 { 00344 int32_t i; 00345 int ret; 00346 00347 ret = 0; 00348 00349 MUTEX_THREAD_LOCK(logp->mutexp); 00350 00351 /* 00352 * Check if we need to grow the table. Note, ndx is 0-based (the 00353 * index into the DB entry table) an dbentry_cnt is 1-based, the 00354 * number of available slots. 00355 */ 00356 if (logp->dbentry_cnt <= ndx) { 00357 if ((ret = CDB___os_realloc(dbenv, 00358 (ndx + DB_GROW_SIZE) * sizeof(DB_ENTRY), 00359 NULL, &logp->dbentry)) != 0) 00360 goto err; 00361 00362 /* Initialize the new entries. */ 00363 for (i = logp->dbentry_cnt; i < ndx + DB_GROW_SIZE; i++) { 00364 logp->dbentry[i].count = 0; 00365 TAILQ_INIT(&logp->dbentry[i].dblist); 00366 logp->dbentry[i].deleted = 0; 00367 logp->dbentry[i].refcount = 0; 00368 } 00369 00370 logp->dbentry_cnt = i; 00371 } 00372 00373 if (logp->dbentry[ndx].deleted == 0 && 00374 TAILQ_FIRST(&logp->dbentry[ndx].dblist) == NULL) { 00375 logp->dbentry[ndx].count = 0; 00376 if (dbp != NULL) 00377 TAILQ_INSERT_HEAD(&logp->dbentry[ndx].dblist, 00378 dbp, links); 00379 logp->dbentry[ndx].deleted = dbp == NULL; 00380 logp->dbentry[ndx].refcount = 1; 00381 } else if (!F_ISSET(logp, DBLOG_RECOVER)) { 00382 if (dbp != NULL) 00383 TAILQ_INSERT_HEAD(&logp->dbentry[ndx].dblist, 00384 dbp, links); 00385 logp->dbentry[ndx].refcount++; 00386 } 00387 00388 err: MUTEX_THREAD_UNLOCK(logp->mutexp); 00389 return (ret); 00390 } 00391 00392 /* 00393 * CDB___db_fileid_to_db -- 00394 * Return the DB corresponding to the specified fileid. 00395 * 00396 * PUBLIC: int CDB___db_fileid_to_db __P((DB_ENV *, DB **, int32_t, int)); 00397 */ 00398 int 00399 CDB___db_fileid_to_db(dbenv, dbpp, ndx, inc) 00400 DB_ENV *dbenv; 00401 DB **dbpp; 00402 int32_t ndx; 00403 int inc; 00404 { 00405 DB_LOG *logp; 00406 DB *dbp; 00407 FNAME *fname; 00408 int ret; 00409 char *name; 00410 00411 ret = 0; 00412 logp = dbenv->lg_handle; 00413 00414 MUTEX_THREAD_LOCK(logp->mutexp); 00415 00416 /* 00417 * Under XA, a process different than the one issuing DB operations 00418 * may abort a transaction. In this case, recovery routines are run 00419 * by a process that does not necessarily have the file open, so we 00420 * we must open the file explicitly. 00421 */ 00422 if (ndx >= logp->dbentry_cnt || 00423 (!logp->dbentry[ndx].deleted && 00424 (dbp = TAILQ_FIRST(&logp->dbentry[ndx].dblist)) == NULL)) { 00425 if (F_ISSET(logp, DBLOG_RECOVER)) { 00426 ret = ENOENT; 00427 goto err; 00428 } 00429 if (__log_lid_to_fname(logp, ndx, &fname) != 0) { 00430 /* Couldn't find entry; this is a fatal error. */ 00431 CDB___db_err(dbenv, "Missing log fileid entry"); 00432 ret = EINVAL; 00433 goto err; 00434 } 00435 name = R_ADDR(&logp->reginfo, fname->name_off); 00436 00437 /* 00438 * __log_do_open is called without protection of the 00439 * log thread lock. 00440 */ 00441 MUTEX_THREAD_UNLOCK(logp->mutexp); 00442 00443 /* 00444 * At this point, we are not holding the thread lock, so exit 00445 * directly instead of going through the exit code at the 00446 * bottom. If the __log_do_open succeeded, then we don't need 00447 * to do any of the remaining error checking at the end of this 00448 * routine. 00449 */ 00450 if ((ret = __log_do_open(dbenv, logp, 00451 fname->ufid, name, fname->s_type, 00452 ndx, fname->meta_pgno)) != 0) 00453 return (ret); 00454 00455 *dbpp = TAILQ_FIRST(&logp->dbentry[ndx].dblist); 00456 return (0); 00457 } 00458 00459 /* 00460 * Return DB_DELETED if the file has been deleted (it's not an error). 00461 */ 00462 if (logp->dbentry[ndx].deleted) { 00463 ret = DB_DELETED; 00464 if (inc) 00465 logp->dbentry[ndx].count++; 00466 goto err; 00467 } 00468 00469 /* 00470 * Otherwise return 0, but if we don't have a corresponding DB, it's 00471 * an error. 00472 */ 00473 if ((*dbpp = TAILQ_FIRST(&logp->dbentry[ndx].dblist)) == NULL) 00474 ret = ENOENT; 00475 00476 err: MUTEX_THREAD_UNLOCK(logp->mutexp); 00477 return (ret); 00478 } 00479 00480 /* 00481 * Close files that were opened by the recovery daemon. 00482 * 00483 * PUBLIC: void CDB___log_close_files __P((DB_ENV *)); 00484 */ 00485 void 00486 CDB___log_close_files(dbenv) 00487 DB_ENV *dbenv; 00488 { 00489 DB_ENTRY *dbe; 00490 DB_LOG *logp; 00491 DB *dbp; 00492 int32_t i; 00493 00494 logp = dbenv->lg_handle; 00495 MUTEX_THREAD_LOCK(logp->mutexp); 00496 for (i = 0; i < logp->dbentry_cnt; i++) { 00497 dbe = &logp->dbentry[i]; 00498 while ((dbp = TAILQ_FIRST(&dbe->dblist)) != NULL) { 00499 (void)CDB_log_unregister(dbenv, dbp); 00500 TAILQ_REMOVE(&dbe->dblist, dbp, links); 00501 (void)dbp->close(dbp, 0); 00502 } 00503 dbe->deleted = 0; 00504 dbe->refcount = 0; 00505 } 00506 MUTEX_THREAD_UNLOCK(logp->mutexp); 00507 } 00508 00509 /* 00510 * CDB___log_rem_logid 00511 * Remove an entry from the log table. Find the appropriate DB and 00512 * unlink it from the linked list off the table. If the DB is NULL, treat 00513 * this as a simple refcount decrement. 00514 * 00515 * PUBLIC: void CDB___log_rem_logid __P((DB_LOG *, DB *, int32_t)); 00516 */ 00517 void 00518 CDB___log_rem_logid(logp, dbp, ndx) 00519 DB_LOG *logp; 00520 DB *dbp; 00521 int32_t ndx; 00522 { 00523 DB *xdbp; 00524 00525 MUTEX_THREAD_LOCK(logp->mutexp); 00526 if (--logp->dbentry[ndx].refcount == 0) { 00527 TAILQ_INIT(&logp->dbentry[ndx].dblist); 00528 logp->dbentry[ndx].deleted = 0; 00529 } else if (dbp != NULL) 00530 for (xdbp = TAILQ_FIRST(&logp->dbentry[ndx].dblist); 00531 xdbp != NULL; 00532 xdbp = TAILQ_NEXT(xdbp, links)) 00533 if (xdbp == dbp) { 00534 TAILQ_REMOVE(&logp->dbentry[ndx].dblist, 00535 xdbp, links); 00536 break; 00537 } 00538 00539 MUTEX_THREAD_UNLOCK(logp->mutexp); 00540 } 00541 00542 /* 00543 * __log_lid_to_fname -- 00544 * Traverse the shared-memory region looking for the entry that 00545 * matches the passed log fileid. Returns 0 on success; -1 on error. 00546 */ 00547 static int 00548 __log_lid_to_fname(dblp, lid, fnamep) 00549 DB_LOG *dblp; 00550 int32_t lid; 00551 FNAME **fnamep; 00552 { 00553 FNAME *fnp; 00554 LOG *lp; 00555 00556 lp = dblp->reginfo.primary; 00557 00558 for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); 00559 fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { 00560 if (fnp->ref == 0) /* Entry not in use. */ 00561 continue; 00562 if (fnp->id == lid) { 00563 *fnamep = fnp; 00564 return (0); 00565 } 00566 } 00567 return (-1); 00568 }