📄 fop_util.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2001-2004 * Sleepycat Software. All rights reserved. * * $Id: fop_util.c,v 1.104 2004/09/24 00:43:18 bostic Exp $ */#include "db_config.h"#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <stdlib.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_shash.h"#include "dbinc/db_am.h"#include "dbinc/fop.h"#include "dbinc/lock.h"#include "dbinc/mp.h"#include "dbinc/log.h"#include "dbinc/txn.h"static int __fop_set_pgsize __P((DB *, DB_FH *, const char *));/* * Acquire the environment meta-data lock. The parameters are the * environment (ENV), the locker id to use in acquiring the lock (ID) * and a pointer to a DB_LOCK. * * !!! * Turn off locking for Critical Path. The application must do its own * synchronization of open/create. Two threads creating and opening a * file at the same time may have unpredictable results. */#ifdef CRITICALPATH_10266#define GET_ENVLOCK(ENV, ID, L) (0)#else#define GET_ENVLOCK(ENV, ID, L) do { \ DBT __dbt; \ u_int32_t __lockval; \ \ if (LOCKING_ON((ENV))) { \ __lockval = 1; \ __dbt.data = &__lockval; \ __dbt.size = sizeof(__lockval); \ if ((ret = __lock_get((ENV), (ID), \ 0, &__dbt, DB_LOCK_WRITE, (L))) != 0) \ goto err; \ } \} while (0)#endif/* * If we open a file handle and our caller is doing fcntl(2) locking, * we can't close the handle because that would discard the caller's * lock. Save it until we close or refresh the DB handle. */#define CLOSE_HANDLE(D, F) { \ if ((F) != NULL) { \ if (LF_ISSET(DB_FCNTL_LOCKING)) \ (D)->saved_open_fhp = (F); \ else if ((t_ret = \ __os_closehandle((D)->dbenv, (F))) != 0) { \ if (ret == 0) \ ret = t_ret; \ goto err; \ } \ (F) = NULL; \ } \}/* * __fop_lock_handle -- * * Get the handle lock for a database. If the envlock is specified, do this * as a lock_vec call that releases the environment lock before acquiring the * handle lock. * * PUBLIC: int __fop_lock_handle __P((DB_ENV *, * PUBLIC: DB *, u_int32_t, db_lockmode_t, DB_LOCK *, u_int32_t)); * */int__fop_lock_handle(dbenv, dbp, locker, mode, elockp, flags) DB_ENV *dbenv; DB *dbp; u_int32_t locker; db_lockmode_t mode; DB_LOCK *elockp; u_int32_t flags;{ DBT fileobj; DB_LOCKREQ reqs[2], *ereq; DB_LOCK_ILOCK lock_desc; int ret; if (!LOCKING_ON(dbenv) || F_ISSET(dbp, DB_AM_COMPENSATE | DB_AM_RECOVER)) return (0); /* * If we are in recovery, the only locking we should be * doing is on the global environment. */ if (IS_RECOVERING(dbenv)) return (elockp == NULL ? 0 : __ENV_LPUT(dbenv, *elockp, 0)); memcpy(lock_desc.fileid, dbp->fileid, DB_FILE_ID_LEN); lock_desc.pgno = dbp->meta_pgno; lock_desc.type = DB_HANDLE_LOCK; memset(&fileobj, 0, sizeof(fileobj)); fileobj.data = &lock_desc; fileobj.size = sizeof(lock_desc); DB_TEST_SUBLOCKS(dbenv, flags); if (elockp == NULL) ret = __lock_get(dbenv, locker, flags, &fileobj, mode, &dbp->handle_lock); else { reqs[0].op = DB_LOCK_PUT; reqs[0].lock = *elockp; reqs[1].op = DB_LOCK_GET; reqs[1].mode = mode; reqs[1].obj = &fileobj; reqs[1].timeout = 0; if ((ret = __lock_vec(dbenv, locker, flags, reqs, 2, &ereq)) == 0) { dbp->handle_lock = reqs[1].lock; LOCK_INIT(*elockp); } else if (ereq != reqs) LOCK_INIT(*elockp); } dbp->cur_lid = locker; return (ret);}/* * __fop_file_setup -- * * Perform all the needed checking and locking to open up or create a * file. * * There's a reason we don't push this code down into the buffer cache. * The problem is that there's no information external to the file that * we can use as a unique ID. UNIX has dev/inode pairs, but they are * not necessarily unique after reboot, if the file was mounted via NFS. * Windows has similar problems, as the FAT filesystem doesn't maintain * dev/inode numbers across reboot. So, we must get something from the * file we can use to ensure that, even after a reboot, the file we're * joining in the cache is the right file for us to join. The solution * we use is to maintain a file ID that's stored in the database, and * that's why we have to open and read the file before calling into the * buffer cache or obtaining a lock (we use this unique fileid to lock * as well as to identify like files in the cache). * * There are a couple of idiosyncrasies that this code must support, in * particular, DB_TRUNCATE and DB_FCNTL_LOCKING. First, we disallow * DB_TRUNCATE in the presence of transactions, since opening a file with * O_TRUNC will result in data being lost in an unrecoverable fashion. * We also disallow DB_TRUNCATE if locking is enabled, because even in * the presence of locking, we cannot avoid race conditions, so allowing * DB_TRUNCATE with locking would be misleading. See SR [#7345] for more * details. * * However, if you are running with neither locking nor transactions, then * you can specify DB_TRUNCATE, and if you do so, we will truncate the file * regardless of its contents. * * FCNTL locking introduces another set of complications. First, the only * reason we support the DB_FCNTL_LOCKING flag is for historic compatibility * with programs like Sendmail and Postfix. In these cases, the caller may * already have a lock on the file; we need to make sure that any file handles * we open remain open, because if we were to close them, the lock held by the * caller would go away. Furthermore, Sendmail and/or Postfix need the ability * to create databases in empty files. So, when you're doing FCNTL locking, * it's reasonable that you are trying to create a database into a 0-length * file and we allow it, while under normal conditions, we do not create * databases if the files already exist and are not Berkeley DB files. * * PUBLIC: int __fop_file_setup __P((DB *, * PUBLIC: DB_TXN *, const char *, int, u_int32_t, u_int32_t *)); */int__fop_file_setup(dbp, txn, name, mode, flags, retidp) DB *dbp; DB_TXN *txn; const char *name; int mode; u_int32_t flags, *retidp;{ DB_ENV *dbenv; DB_FH *fhp; DB_LOCK elock; DB_TXN *stxn; size_t len; u_int32_t dflags, locker, oflags; u_int8_t mbuf[DBMETASIZE]; int created_locker, ret, retries, t_ret, tmp_created, truncating; char *real_name, *real_tmpname, *tmpname; DB_ASSERT(name != NULL); *retidp = TXN_INVALID; dbenv = dbp->dbenv; fhp = NULL; LOCK_INIT(elock); stxn = NULL; created_locker = tmp_created = truncating = 0; real_name = real_tmpname = tmpname = NULL; dflags = F_ISSET(dbp, DB_AM_NOT_DURABLE) ? DB_LOG_NOT_DURABLE : 0; /* * Get a lockerid for this handle. There are paths through queue * rename and remove where this dbp already has a locker, so make * sure we don't clobber it and conflict. */ if (LOCKING_ON(dbenv) && !F_ISSET(dbp, DB_AM_COMPENSATE) && !F_ISSET(dbp, DB_AM_RECOVER) && dbp->lid == DB_LOCK_INVALIDID) { if ((ret = __lock_id(dbenv, &dbp->lid)) != 0) goto err; created_locker = 1; } LOCK_INIT(dbp->handle_lock); locker = txn == NULL ? dbp->lid : txn->txnid; /* Get the real backing file name. */ if ((ret = __db_appname(dbenv, DB_APP_DATA, name, 0, NULL, &real_name)) != 0) goto err; /* Fill in the default file mode. */ if (mode == 0) mode = __db_omode("rwrw--"); oflags = 0; if (LF_ISSET(DB_RDONLY)) oflags |= DB_OSO_RDONLY; if (LF_ISSET(DB_TRUNCATE)) oflags |= DB_OSO_TRUNC; retries = 0;retry: /* * If we cannot create the file, only retry a few times. We * think we might be in a race with another create, but it could * be that the backup filename exists (that is, is left over from * a previous crash). */ if (++retries > DB_RETRY) { __db_err(dbenv, "__fop_file_setup: Retry limit (%d) exceeded", DB_RETRY); goto err; } if (!F_ISSET(dbp, DB_AM_COMPENSATE) && !F_ISSET(dbp, DB_AM_RECOVER)) GET_ENVLOCK(dbenv, locker, &elock); if ((ret = __os_exists(real_name, NULL)) == 0) { /* * If the file exists, there are 5 possible cases: * 1. DB_EXCL was specified so this is an error, unless * this is a file left around after a rename and we * are in the same transaction. This gets decomposed * into several subcases, because we check for various * errors before we know we're in rename. * 2. We are truncating, and it doesn't matter what kind * of file it is, we should open/create it. * 3. It is 0-length, we are not doing transactions (i.e., * we are sendmail), we should open/create into it. * 4. Is it a Berkeley DB file and we should simply open it. * 5. It is not a BDB file and we should return an error. */ /* We have to open the file. */reopen: if ((ret = __os_open(dbenv, real_name, oflags, 0, &fhp)) != 0) goto err; /* Case 2: DB_TRUNCATE: we must do the creation in place. */ if (LF_ISSET(DB_TRUNCATE)) { if (LF_ISSET(DB_EXCL)) { /* Case 1a: DB_EXCL and DB_TRUNCATE. */ ret = EEXIST; goto err; } tmpname = (char *)name; goto creat2; } /* Cases 1,3-5: we need to read the meta-data page. */ ret = __fop_read_meta(dbenv, real_name, mbuf, sizeof(mbuf), fhp, LF_ISSET(DB_FCNTL_LOCKING) && txn == NULL ? 1 : 0, &len); /* Case 3: 0-length, no txns. */ if (ret != 0 && len == 0 && txn == NULL) { if (LF_ISSET(DB_EXCL)) { /* Case 1b: DB_EXCL and 0-lenth file exists. */ ret = EEXIST; goto err; } tmpname = (char *)name; goto creat2; } /* Case 5: Invalid file. */ if (ret != 0) goto err; /* Case 4: This is a valid file. */ if ((ret = __db_meta_setup(dbenv, dbp, real_name, (DBMETA *)mbuf, flags, 1)) != 0) goto err; /* Now, get our handle lock. */ if ((ret = __fop_lock_handle(dbenv, dbp, locker, DB_LOCK_READ, NULL, DB_LOCK_NOWAIT)) == 0) { if ((ret = __ENV_LPUT(dbenv, elock, 0)) != 0) goto err; } else if (ret != DB_LOCK_NOTGRANTED || (txn != NULL && F_ISSET(txn, TXN_NOWAIT))) goto err; else { /* * We were unable to acquire the handle lock without * blocking. The fact that we are blocking might mean * that someone else is trying to delete the file. * Since some platforms cannot delete files while they * are open (Windows), we are going to have to close * the file. This would be a problem if we were doing * FCNTL locking, because our closing the handle would * release the FCNTL locks. Fortunately, if we are * doing FCNTL locking, then we should never fail to * acquire our handle lock, so we should never get here. * We assert it here to make sure we aren't destroying * any application level FCNTL semantics. */ DB_ASSERT(!LF_ISSET(DB_FCNTL_LOCKING)); if ((ret = __os_closehandle(dbenv, fhp)) != 0) goto err; fhp = NULL; ret = __fop_lock_handle(dbenv, dbp, locker, DB_LOCK_READ, &elock, 0); if (ret == DB_LOCK_NOTEXIST) goto retry; if (ret != 0) goto err; /* * XXX * I need to convince myself that I don't need to * re-read the metadata page here. If you do need * to re-read it you'd better decrypt it too... */ if ((ret = __os_open(dbenv, real_name, 0, 0, &fhp)) != 0) goto err; } /* If we got here, then we now have the handle lock. */ /* * Check for a file in the midst of a rename. If we find that * the file is in the midst of a rename, it must be the case * that it is in our current transaction (else we would still * be blocking), so we can continue along and create a new file * with the same name. In that case, we have to close the file * handle because we reuse it below. */ if (F_ISSET(dbp, DB_AM_IN_RENAME)) { if (LF_ISSET(DB_CREATE)) { if ((ret = __os_closehandle(dbenv, fhp)) != 0) goto err; goto create; } else { ret = ENOENT; goto err; } } /* * Now, case 1: check for DB_EXCL, because the file that exists * is not in the middle of a rename, so we have an error. This * is a weird case, but we need to make sure that we don't * continue to hold the handle lock, since technically, we * should not have been allowed to open it. */ if (LF_ISSET(DB_EXCL)) { ret = __ENV_LPUT(dbenv, dbp->handle_lock, 0); LOCK_INIT(dbp->handle_lock); if (ret == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -