1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 */ 6/* 7 * Copyright (c) 1995, 1996 8 * The President and Fellows of Harvard University. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $Id: dbreg_rec.c,v 12.26 2008/01/08 20:58:19 bostic Exp $ 35 */ 36 37#include "db_config.h" 38 39#include "db_int.h" 40#include "dbinc/db_page.h" 41#include "dbinc/db_am.h" 42#include "dbinc/log.h" 43#include "dbinc/txn.h" 44 45static int __dbreg_open_file __P((ENV *, 46 DB_TXN *, __dbreg_register_args *, void *)); 47 48/* 49 * PUBLIC: int __dbreg_register_recover 50 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 51 */ 52int 53__dbreg_register_recover(env, dbtp, lsnp, op, info) 54 ENV *env; 55 DBT *dbtp; 56 DB_LSN *lsnp; 57 db_recops op; 58 void *info; 59{ 60 __dbreg_register_args *argp; 61 DB_ENTRY *dbe; 62 DB_LOG *dblp; 63 DB *dbp; 64 u_int32_t status; 65 int do_close, do_open, do_rem, ret, t_ret; 66 67 dblp = env->lg_handle; 68 dbp = NULL; 69 70#ifdef DEBUG_RECOVER 71 REC_PRINT(__dbreg_register_print); 72#endif 73 do_open = do_close = 0; 74 if ((ret = __dbreg_register_read(env, dbtp->data, &argp)) != 0) 75 goto out; 76 77 switch (argp->opcode) { 78 case DBREG_REOPEN: 79 case DBREG_PREOPEN: 80 case DBREG_OPEN: 81 /* 82 * In general, we redo the open on REDO and abort on UNDO. 83 * However, a reopen is a second instance of an open of 84 * in-memory files and we don't want to close them yet 85 * on abort, so just skip that here. 86 */ 87 if ((DB_REDO(op) || 88 op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES)) 89 do_open = 1; 90 else if (argp->opcode != DBREG_REOPEN) 91 do_close = 1; 92 break; 93 case DBREG_CLOSE: 94 if (DB_UNDO(op)) 95 do_open = 1; 96 else 97 do_close = 1; 98 break; 99 case DBREG_RCLOSE: 100 /* 101 * DBREG_RCLOSE was generated by recover because a file was 102 * left open. The POPENFILES pass, which is run to open 103 * files to abort prepared transactions, may not include the 104 * open for this file so we open it here. Note that a normal 105 * CLOSE is not legal before the prepared transaction is 106 * committed or aborted. 107 */ 108 if (DB_UNDO(op) || op == DB_TXN_POPENFILES) 109 do_open = 1; 110 else 111 do_close = 1; 112 break; 113 case DBREG_CHKPNT: 114 if (DB_UNDO(op) || 115 op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES) 116 do_open = 1; 117 break; 118 default: 119 ret = __db_unknown_path(env, "__dbreg_register_recover"); 120 goto out; 121 } 122 123 if (do_open) { 124 /* 125 * We must open the db even if the meta page is not 126 * yet written as we may be creating subdatabase. 127 */ 128 if (op == DB_TXN_OPENFILES && argp->opcode != DBREG_CHKPNT) 129 F_SET(dblp, DBLOG_FORCE_OPEN); 130 131 /* 132 * During an abort or an open pass to recover prepared txns, 133 * we need to make sure that we use the same locker id on the 134 * open. We pass the txnid along to ensure this. 135 */ 136 ret = __dbreg_open_file(env, 137 op == DB_TXN_ABORT || op == DB_TXN_POPENFILES ? 138 argp->txnp : NULL, argp, info); 139 if (ret == DB_PAGE_NOTFOUND && argp->meta_pgno != PGNO_BASE_MD) 140 ret = ENOENT; 141 if (ret == ENOENT || ret == EINVAL) { 142 /* 143 * If this is an OPEN while rolling forward, it's 144 * possible that the file was recreated since last 145 * time we got here. In that case, we've got deleted 146 * set and probably shouldn't, so we need to check 147 * for that case and possibly retry. 148 */ 149 if (op == DB_TXN_FORWARD_ROLL && 150 argp->txnp != 0 && 151 dblp->dbentry[argp->fileid].deleted) { 152 dblp->dbentry[argp->fileid].deleted = 0; 153 ret = 154 __dbreg_open_file(env, NULL, argp, info); 155 if (ret == DB_PAGE_NOTFOUND && 156 argp->meta_pgno != PGNO_BASE_MD) 157 ret = ENOENT; 158 } 159 /* 160 * We treat ENOENT as OK since it's possible that 161 * the file was renamed or deleted. 162 * All other errors, we return. 163 */ 164 if (ret == ENOENT) 165 ret = 0; 166 } 167 F_CLR(dblp, DBLOG_FORCE_OPEN); 168 } 169 170 if (do_close) { 171 /* 172 * If we are undoing an open, or redoing a close, 173 * then we need to close the file. If we are simply 174 * revoking then we just need to grab the DBP and revoke 175 * the log id. 176 * 177 * If the file is deleted, then we can just ignore this close. 178 * Otherwise, we should usually have a valid dbp we should 179 * close or whose reference count should be decremented. 180 * However, if we shut down without closing a file, we may, in 181 * fact, not have the file open, and that's OK. 182 */ 183 do_rem = 0; 184 MUTEX_LOCK(env, dblp->mtx_dbreg); 185 if (argp->fileid < dblp->dbentry_cnt) { 186 /* 187 * Typically, closes should match an open which means 188 * that if this is a close, there should be a valid 189 * entry in the dbentry table when we get here, 190 * however there are exceptions. 1. If this is an 191 * OPENFILES pass, then we may have started from 192 * a log file other than the first, and the 193 * corresponding open appears in an earlier file. 194 * 2. If we are undoing an open on an abort or 195 * recovery, it's possible that we failed after 196 * the log record, but before we actually entered 197 * a handle here. 198 * 3. If we aborted an open, then we wrote a non-txnal 199 * RCLOSE into the log. During the forward pass, the 200 * file won't be open, and that's OK. 201 */ 202 dbe = &dblp->dbentry[argp->fileid]; 203 if (dbe->dbp == NULL && !dbe->deleted) { 204 /* No valid entry here. */ 205 if ((DB_REDO(op) && 206 argp->opcode != DBREG_RCLOSE) || 207 argp->opcode == DBREG_CHKPNT) { 208 __db_errx(env, 209 "Warning: Improper file close at %lu/%lu", 210 (u_long)lsnp->file, 211 (u_long)lsnp->offset); 212 } 213 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 214 goto done; 215 } 216 217 /* We have either an open entry or a deleted entry. */ 218 if ((dbp = dbe->dbp) != NULL) { 219 /* 220 * If we're a replication client, it's 221 * possible to get here with a dbp that 222 * the user opened, but which we later 223 * assigned a fileid to. Be sure that 224 * we only close dbps that we opened in 225 * the recovery code or that were opened 226 * inside a currently aborting transaction 227 * but not by the recovery code. 228 */ 229 do_rem = F_ISSET(dbp, DB_AM_RECOVER) ? 230 op != DB_TXN_ABORT : op == DB_TXN_ABORT; 231 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 232 } else if (dbe->deleted) { 233 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 234 if ((ret = __dbreg_rem_dbentry( 235 dblp, argp->fileid)) != 0) 236 goto out; 237 } 238 } else 239 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 240 241 /* 242 * During recovery, all files are closed. On an abort, we only 243 * close the file if we opened it during the abort 244 * (DB_AM_RECOVER set), otherwise we simply do a __db_refresh. 245 * For the close case, if remove or rename has closed the file, 246 * don't request a sync, because a NULL mpf would be a problem. 247 * 248 * If we are undoing a create we'd better discard any buffers 249 * from the memory pool. We identify creates because the 250 * argp->id field contains the transaction containing the file 251 * create; if that id is invalid, we are not creating. 252 * 253 * On the backward pass, we need to "undo" opens even if the 254 * transaction in which they appeared committed, because we have 255 * already undone the corresponding close. In that case, the 256 * id will be valid, but we do not want to discard buffers. 257 */ 258 if (do_rem && dbp != NULL) { 259 if (argp->id != TXN_INVALID) { 260 if ((ret = __db_txnlist_find(env, 261 info, argp->txnp->txnid, &status)) 262 != DB_NOTFOUND && ret != 0) 263 goto out; 264 if (ret == DB_NOTFOUND || status != TXN_COMMIT) 265 F_SET(dbp, DB_AM_DISCARD); 266 ret = 0; 267 } 268 269 if (op == DB_TXN_ABORT) { 270 if ((t_ret = __db_refresh(dbp, 271 NULL, DB_NOSYNC, NULL, 0)) != 0 && ret == 0) 272 ret = t_ret; 273 } else { 274 if ((t_ret = __db_close( 275 dbp, NULL, DB_NOSYNC)) != 0 && ret == 0) 276 ret = t_ret; 277 } 278 } 279 } 280done: if (ret == 0) 281 *lsnp = argp->prev_lsn; 282out: if (argp != NULL) 283 __os_free(env, argp); 284 return (ret); 285} 286 287/* 288 * __dbreg_open_file -- 289 * Called during log_register recovery. Make sure that we have an 290 * entry in the dbentry table for this ndx. Returns 0 on success, 291 * non-zero on error. 292 */ 293static int 294__dbreg_open_file(env, txn, argp, info) 295 ENV *env; 296 DB_TXN *txn; 297 __dbreg_register_args *argp; 298 void *info; 299{ 300 DB *dbp; 301 DB_ENTRY *dbe; 302 DB_LOG *dblp; 303 u_int32_t id, status; 304 int ret; 305 306 dblp = env->lg_handle; 307 308 /* 309 * When we're opening, we have to check that the name we are opening 310 * is what we expect. If it's not, then we close the old file and 311 * open the new one. 312 */ 313 MUTEX_LOCK(env, dblp->mtx_dbreg); 314 if (argp->fileid != DB_LOGFILEID_INVALID && 315 argp->fileid < dblp->dbentry_cnt) 316 dbe = &dblp->dbentry[argp->fileid]; 317 else 318 dbe = NULL; 319 320 if (dbe != NULL) { 321 if (dbe->deleted) { 322 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 323 return (ENOENT); 324 } 325 326 /* 327 * At the end of OPENFILES, we may have a file open. If this 328 * is a reopen, then we will always close and reopen. If the 329 * open was part of a committed transaction, so it doesn't 330 * get undone. However, if the fileid was previously used, 331 * we'll see a close that may need to get undone. There are 332 * three ways we can detect this. 1) the meta-pgno in the 333 * current file does not match that of the open file, 2) the 334 * file uid of the current file does not match that of the 335 * previously opened file, 3) the current file is unnamed, in 336 * which case it should never be opened during recovery. 337 */ 338 if ((dbp = dbe->dbp) != NULL) { 339 if (argp->opcode == DBREG_REOPEN || 340 dbp->meta_pgno != argp->meta_pgno || 341 argp->name.size == 0 || 342 memcmp(dbp->fileid, argp->uid.data, 343 DB_FILE_ID_LEN) != 0) { 344 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 345 (void)__dbreg_revoke_id(dbp, 0, 346 DB_LOGFILEID_INVALID); 347 if (F_ISSET(dbp, DB_AM_RECOVER)) 348 (void)__db_close(dbp, NULL, DB_NOSYNC); 349 goto reopen; 350 } 351 352 /* 353 * We should only get here if we already have the 354 * dbp from an openfiles pass, in which case, what's 355 * here had better be the same dbp. 356 */ 357 DB_ASSERT(env, dbe->dbp == dbp); 358 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 359 360 /* 361 * This is a successful open. We need to record that 362 * in the txnlist so that we know how to handle the 363 * subtransaction that created the file system object. 364 */ 365 if (argp->id != TXN_INVALID && 366 (ret = __db_txnlist_update(env, info, 367 argp->id, TXN_EXPECTED, NULL, &status, 1)) != 0) 368 return (ret); 369 return (0); 370 } 371 } 372 373 MUTEX_UNLOCK(env, dblp->mtx_dbreg); 374 375reopen: 376 /* 377 * We never re-open temporary files. Temp files are only useful during 378 * aborts in which case the dbp was entered when the file was 379 * registered. During recovery, we treat temp files as properly deleted 380 * files, allowing the open to fail and not reporting any errors when 381 * recovery fails to get a valid dbp from __dbreg_id_to_db. 382 */ 383 if (argp->name.size == 0) { 384 (void)__dbreg_add_dbentry(env, dblp, NULL, argp->fileid); 385 return (ENOENT); 386 } 387 388 /* 389 * We are about to pass a recovery txn pointer into the main library. 390 * We need to make sure that any accessed fields are set appropriately. 391 */ 392 if (txn != NULL) { 393 id = txn->txnid; 394 memset(txn, 0, sizeof(DB_TXN)); 395 txn->txnid = id; 396 txn->mgrp = env->tx_handle; 397 } 398 399 return (__dbreg_do_open(env, 400 txn, dblp, argp->uid.data, argp->name.data, argp->ftype, 401 argp->fileid, argp->meta_pgno, info, argp->id, argp->opcode)); 402} 403