Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

dbreg_rec.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
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  * $Id: dbreg_rec.c,v 12.8 2005/11/09 14:20:32 margo Exp $
00036  */
00037 
00038 #include "db_config.h"
00039 
00040 #ifndef NO_SYSTEM_INCLUDES
00041 #include <sys/types.h>
00042 
00043 #include <string.h>
00044 #endif
00045 
00046 #include "db_int.h"
00047 #include "dbinc/db_page.h"
00048 #include "dbinc/db_shash.h"
00049 #include "dbinc/db_am.h"
00050 #include "dbinc/log.h"
00051 #include "dbinc/mp.h"
00052 #include "dbinc/txn.h"
00053 
00054 static int __dbreg_open_file __P((DB_ENV *,
00055     DB_TXN *, __dbreg_register_args *, void *));
00056 
00057 /*
00058  * PUBLIC: int __dbreg_register_recover
00059  * PUBLIC:     __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *));
00060  */
00061 int
00062 __dbreg_register_recover(dbenv, dbtp, lsnp, op, info)
00063         DB_ENV *dbenv;
00064         DBT *dbtp;
00065         DB_LSN *lsnp;
00066         db_recops op;
00067         void *info;
00068 {
00069         DB_ENTRY *dbe;
00070         DB_LOG *dblp;
00071         DB *dbp;
00072         __dbreg_register_args *argp;
00073         int do_close, do_open, do_rem, ret, t_ret;
00074         u_int32_t status;
00075 
00076         dblp = dbenv->lg_handle;
00077         dbp = NULL;
00078 
00079 #ifdef DEBUG_RECOVER
00080         REC_PRINT(__dbreg_register_print);
00081 #endif
00082         do_open = do_close = 0;
00083         if ((ret = __dbreg_register_read(dbenv, dbtp->data, &argp)) != 0)
00084                 goto out;
00085 
00086         switch (argp->opcode) {
00087         case DBREG_REOPEN:
00088         case DBREG_PREOPEN:
00089         case DBREG_OPEN:
00090                 /*
00091                  * In general, we redo the open on REDO and abort on UNDO.
00092                  * However, a reopen is a second instance of an open of
00093                  * in-memory files and we don't want to close them yet
00094                  * on abort, so just skip that here.
00095                  */
00096                 if ((DB_REDO(op) ||
00097                     op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES))
00098                         do_open = 1;
00099                 else if (argp->opcode != DBREG_REOPEN)
00100                         do_close = 1;
00101                 break;
00102         case DBREG_CLOSE:
00103                 if (DB_UNDO(op))
00104                         do_open = 1;
00105                 else
00106                         do_close = 1;
00107                 break;
00108         case DBREG_RCLOSE:
00109                 /*
00110                  * DBREG_RCLOSE was generated by recover because a file was
00111                  * left open.  The POPENFILES pass, which is run to open
00112                  * files to abort prepared transactions, may not include the
00113                  * open for this file so we open it here.  Note that a normal
00114                  * CLOSE is not legal before the prepared transaction is
00115                  * committed or aborted.
00116                  */
00117                 if (DB_UNDO(op) || op == DB_TXN_POPENFILES)
00118                         do_open = 1;
00119                 else
00120                         do_close = 1;
00121                 break;
00122         case DBREG_CHKPNT:
00123                 if (DB_UNDO(op) ||
00124                     op == DB_TXN_OPENFILES || op == DB_TXN_POPENFILES)
00125                         do_open = 1;
00126                 break;
00127         default:
00128                 DB_ASSERT(0);
00129                 ret = EINVAL;
00130                 break;
00131         }
00132 
00133         if (do_open) {
00134                 /*
00135                  * We must open the db even if the meta page is not
00136                  * yet written as we may be creating subdatabase.
00137                  */
00138                 if (op == DB_TXN_OPENFILES && argp->opcode != DBREG_CHKPNT)
00139                         F_SET(dblp, DBLOG_FORCE_OPEN);
00140 
00141                 /*
00142                  * During an abort or an open pass to recover prepared txns,
00143                  * we need to make sure that we use the same locker id on the
00144                  * open.  We pass the txnid along to ensure this.
00145                  */
00146                 ret = __dbreg_open_file(dbenv,
00147                     op == DB_TXN_ABORT || op == DB_TXN_POPENFILES ?
00148                     argp->txnid : NULL, argp, info);
00149                 if (ret == DB_PAGE_NOTFOUND && argp->meta_pgno != PGNO_BASE_MD)
00150                         ret = ENOENT;
00151                 if (ret == ENOENT || ret == EINVAL) {
00152                         /*
00153                          * If this is an OPEN while rolling forward, it's
00154                          * possible that the file was recreated since last
00155                          * time we got here.  In that case, we've got deleted
00156                          * set and probably shouldn't, so we need to check
00157                          * for that case and possibly retry.
00158                          */
00159                         if (op == DB_TXN_FORWARD_ROLL &&
00160                             argp->txnid != 0 &&
00161                             dblp->dbentry[argp->fileid].deleted) {
00162                                 dblp->dbentry[argp->fileid].deleted = 0;
00163                                 ret =
00164                                     __dbreg_open_file(dbenv, NULL, argp, info);
00165                                 if (ret == DB_PAGE_NOTFOUND &&
00166                                      argp->meta_pgno != PGNO_BASE_MD)
00167                                         ret = ENOENT;
00168                         }
00169                         /*
00170                          * We treat ENOENT as OK since it's possible that
00171                          * the file was renamed or deleted.
00172                          * All other errors, we return.
00173                          */
00174                         if (ret == ENOENT)
00175                                 ret = 0;
00176                 }
00177                 F_CLR(dblp, DBLOG_FORCE_OPEN);
00178         }
00179 
00180         if (do_close) {
00181                 /*
00182                  * If we are undoing an open, or redoing a close,
00183                  * then we need to close the file.  If we are simply
00184                  * revoking then we just need to grab the DBP and revoke
00185                  * the log id.
00186                  *
00187                  * If the file is deleted, then we can just ignore this close.
00188                  * Otherwise, we should usually have a valid dbp we should
00189                  * close or whose reference count should be decremented.
00190                  * However, if we shut down without closing a file, we may, in
00191                  * fact, not have the file open, and that's OK.
00192                  */
00193                 do_rem = 0;
00194                 MUTEX_LOCK(dbenv, dblp->mtx_dbreg);
00195                 if (argp->fileid < dblp->dbentry_cnt) {
00196                         /*
00197                          * Typically, closes should match an open which means
00198                          * that if this is a close, there should be a valid
00199                          * entry in the dbentry table when we get here,
00200                          * however there are exceptions.  1. If this is an
00201                          * OPENFILES pass, then we may have started from
00202                          * a log file other than the first, and the
00203                          * corresponding open appears in an earlier file.
00204                          * 2. If we are undoing an open on an abort or
00205                          * recovery, it's possible that we failed after
00206                          * the log record, but before we actually entered
00207                          * a handle here.
00208                          * 3. If we aborted an open, then we wrote a non-txnal
00209                          * RCLOSE into the log.  During the forward pass, the
00210                          * file won't be open, and that's OK.
00211                          */
00212                         dbe = &dblp->dbentry[argp->fileid];
00213                         if (dbe->dbp == NULL && !dbe->deleted) {
00214                                 /* No valid entry here. */
00215                                 if ((DB_REDO(op) &&
00216                                     argp->opcode != DBREG_RCLOSE) ||
00217                                     argp->opcode == DBREG_CHKPNT) {
00218                                         __db_err(dbenv,
00219                                     "Warning: Improper file close at %lu/%lu",
00220                                             (u_long)lsnp->file,
00221                                             (u_long)lsnp->offset);
00222                                 }
00223                                 MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00224                                 goto done;
00225                         }
00226 
00227                         /* We have either an open entry or a deleted entry. */
00228                         if ((dbp = dbe->dbp) != NULL) {
00229                                 /*
00230                                  * If we're a replication client, it's
00231                                  * possible to get here with a dbp that
00232                                  * the user opened, but which we later
00233                                  * assigned a fileid to.  Be sure that
00234                                  * we only close dbps that we opened in
00235                                  * the recovery code or that were opened
00236                                  * inside a currently aborting transaction.
00237                                  */
00238                                 do_rem = F_ISSET(dbp, DB_AM_RECOVER) ||
00239                                     op == DB_TXN_ABORT;
00240                                 MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00241                                 if (op == DB_TXN_ABORT)
00242                                         (void)__dbreg_close_id(dbp,
00243                                             NULL, DBREG_RCLOSE);
00244                                 else
00245                                         (void)__dbreg_revoke_id(dbp, 0,
00246                                             DB_LOGFILEID_INVALID);
00247                         } else if (dbe->deleted) {
00248                                 MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00249                                 if ((ret = __dbreg_rem_dbentry(
00250                                     dblp, argp->fileid)) != 0)
00251                                         goto out;
00252                         }
00253                 } else
00254                         MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00255 
00256                 /*
00257                  * During recovery, all files are closed.  On an abort, we only
00258                  * close the file if we opened it during the abort
00259                  * (DB_AM_RECOVER set), otherwise we simply do a __db_refresh.
00260                  * For the close case, if remove or rename has closed the file,
00261                  * don't request a sync, because a NULL mpf would be a problem.
00262                  *
00263                  * If we are undoing a create we'd better discard any buffers
00264                  * from the memory pool.  We identify creates because the
00265                  * argp->id field contains the transaction containing the file
00266                  * create; if that id is invalid, we are not creating.
00267                  *
00268                  * On the backward pass, we need to "undo" opens even if the
00269                  * transaction in which they appeared committed, because we have
00270                  * already undone the corresponding close.  In that case, the
00271                  * id will be valid, but we do not want to discard buffers.
00272                  */
00273                 if (do_rem && dbp != NULL) {
00274                         if (argp->id != TXN_INVALID) {
00275                                 if ((ret = __db_txnlist_find(dbenv,
00276                                     info, argp->txnid->txnid, &status))
00277                                     != DB_NOTFOUND && ret != 0)
00278                                         goto out;
00279                                 if (ret == DB_NOTFOUND || status != TXN_COMMIT)
00280                                         F_SET(dbp, DB_AM_DISCARD);
00281                                 ret = 0;
00282                         }
00283 
00284                         if (op == DB_TXN_ABORT &&
00285                             !F_ISSET(dbp, DB_AM_RECOVER)) {
00286                                 if ((t_ret = __db_refresh(dbp,
00287                                     NULL, DB_NOSYNC, NULL, 0)) != 0 && ret == 0)
00288                                         ret = t_ret;
00289                         } else {
00290                                 if (op == DB_TXN_APPLY &&
00291                                     (t_ret = __db_sync(dbp)) != 0 && ret == 0)
00292                                         ret = t_ret;
00293                                 if ((t_ret = __db_close(
00294                                     dbp, NULL, DB_NOSYNC)) != 0 && ret == 0)
00295                                         ret = t_ret;
00296                         }
00297                 }
00298         }
00299 done:   if (ret == 0)
00300                 *lsnp = argp->prev_lsn;
00301 out:    if (argp != NULL)
00302                 __os_free(dbenv, argp);
00303         return (ret);
00304 }
00305 
00306 /*
00307  * __dbreg_open_file --
00308  *      Called during log_register recovery.  Make sure that we have an
00309  *      entry in the dbentry table for this ndx.  Returns 0 on success,
00310  *      non-zero on error.
00311  */
00312 static int
00313 __dbreg_open_file(dbenv, txn, argp, info)
00314         DB_ENV *dbenv;
00315         DB_TXN *txn;
00316         __dbreg_register_args *argp;
00317         void *info;
00318 {
00319         DB_ENTRY *dbe;
00320         DB_LOG *dblp;
00321         DB *dbp;
00322         u_int32_t id, status;
00323         int ret;
00324 
00325         dblp = (DB_LOG *)dbenv->lg_handle;
00326 
00327         /*
00328          * When we're opening, we have to check that the name we are opening
00329          * is what we expect.  If it's not, then we close the old file and
00330          * open the new one.
00331          */
00332         MUTEX_LOCK(dbenv, dblp->mtx_dbreg);
00333         if (argp->fileid != DB_LOGFILEID_INVALID &&
00334             argp->fileid < dblp->dbentry_cnt)
00335                 dbe = &dblp->dbentry[argp->fileid];
00336         else
00337                 dbe = NULL;
00338 
00339         if (dbe != NULL) {
00340                 if (dbe->deleted) {
00341                         MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00342                         return (ENOENT);
00343                 }
00344 
00345                 /*
00346                  * At the end of OPENFILES, we may have a file open.  If this
00347                  * is a reopen, then we will always close and reopen.  If the
00348                  * open was part of a committed transaction, so it doesn't
00349                  * get undone.  However, if the fileid was previously used,
00350                  * we'll see a close that may need to get undone.  There are
00351                  * three ways we can detect this. 1) the meta-pgno in the
00352                  * current file does not match that of the open file, 2) the
00353                  * file uid of the current file does not match that of the
00354                  * previously opened file, 3) the current file is unnamed, in
00355                  * which case it should never be opened during recovery.
00356                  */
00357                 if ((dbp = dbe->dbp) != NULL) {
00358                         if (argp->opcode == DBREG_REOPEN ||
00359                             dbp->meta_pgno != argp->meta_pgno ||
00360                             argp->name.size == 0 ||
00361                             memcmp(dbp->fileid, argp->uid.data,
00362                             DB_FILE_ID_LEN) != 0) {
00363                                 MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00364                                 (void)__dbreg_revoke_id(dbp, 0,
00365                                     DB_LOGFILEID_INVALID);
00366                                 if (F_ISSET(dbp, DB_AM_RECOVER))
00367                                         (void)__db_close(dbp, NULL, DB_NOSYNC);
00368                                 goto reopen;
00369                         }
00370 
00371                         /*
00372                          * We should only get here if we already have the
00373                          * dbp from an openfiles pass, in which case, what's
00374                          * here had better be the same dbp.
00375                          */
00376                         DB_ASSERT(dbe->dbp == dbp);
00377                         MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00378 
00379                         /*
00380                          * This is a successful open.  We need to record that
00381                          * in the txnlist so that we know how to handle the
00382                          * subtransaction that created the file system object.
00383                          */
00384                         if (argp->id != TXN_INVALID &&
00385                             (ret = __db_txnlist_update(dbenv, info,
00386                             argp->id, TXN_EXPECTED, NULL, &status, 1)) != 0)
00387                                 return (ret);
00388                         return (0);
00389                 }
00390         }
00391 
00392         MUTEX_UNLOCK(dbenv, dblp->mtx_dbreg);
00393 
00394 reopen:
00395         /*
00396          * We never re-open temporary files.  Temp files are only useful during
00397          * aborts in which case the dbp was entered when the file was
00398          * registered. During recovery, we treat temp files as properly deleted
00399          * files, allowing the open to fail and not reporting any errors when
00400          * recovery fails to get a valid dbp from __dbreg_id_to_db.
00401          */
00402         if (argp->name.size == 0) {
00403                 (void)__dbreg_add_dbentry(dbenv, dblp, NULL, argp->fileid);
00404                 return (ENOENT);
00405         }
00406 
00407         /*
00408          * We are about to pass a recovery txn pointer into the main library.
00409          * We need to make sure that any accessed fields are set appropriately.
00410          */
00411         if (txn != NULL) {
00412                 id = txn->txnid;
00413                 memset(txn, 0, sizeof(DB_TXN));
00414                 txn->txnid = id;
00415                 txn->mgrp = dbenv->tx_handle;
00416         }
00417 
00418         return (__dbreg_do_open(dbenv,
00419             txn, dblp, argp->uid.data, argp->name.data, argp->ftype,
00420             argp->fileid, argp->meta_pgno, info, argp->id, argp->opcode));
00421 }

Generated on Sun Dec 25 12:14:24 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2