From 6c47f45c43a5885e4192cf77d03e9d0142e0a6be Mon Sep 17 00:00:00 2001 From: Date: Wed, 12 May 2010 18:01:40 +0000 Subject: [PATCH] Refactoring the VFS-SHM methods used by WAL. This version compiles and runs non-WAL test cases but crashes and burns on wal.test. --- doc/vfs-shm.txt | 18 +- src/os.c | 18 + src/os.h | 6 + src/os_unix.c | 3453 ++++++++++++++++++++++---------------------- src/pager.c | 24 +- src/pager.h | 1 + src/sqlite.h.in | 15 +- src/test6.c | 71 +- src/test_devsym.c | 72 +- src/test_onefile.c | 19 +- src/test_vfs.c | 58 +- src/vdbe.c | 4 +- src/wal.c | 104 +- src/wal.h | 5 +- 14 files changed, 1940 insertions(+), 1928 deletions(-) diff --git a/doc/vfs-shm.txt b/doc/vfs-shm.txt index 93ab457cd..c1f125a12 100644 --- a/doc/vfs-shm.txt +++ b/doc/vfs-shm.txt @@ -78,14 +78,24 @@ during testing using an instrumented lock manager. (5) No part of the wal-index will be read without holding either some kind of SHM lock or an EXCLUSIVE lock on the original database. + The original database is the file named in the 2nd parameter to + the xShmOpen method. + (6) A holder of a READ_FULL will never read any page of the database file that is contained anywhere in the wal-index. + (7) No part of the wal-index other than the header will be written nor - will the size of the wal-index grow without holding a WRITE. + will the size of the wal-index grow without holding a WRITE or + an EXCLUSIVE on the original database file. + (8) The wal-index header will not be written without holding one of - WRITE, CHECKPOINT, or RECOVER. -(9) A CHECKPOINT or RECOVER must be held in order to reset the last valid - frame counter in the header of the wal-index back to zero. + WRITE, CHECKPOINT, or RECOVER on the wal-index or an EXCLUSIVE on + the original database files. + +(9) A CHECKPOINT or RECOVER must be held on the wal-index, or an + EXCLUSIVE on the original database file, in order to reset the + last valid frame counter in the header of the wal-index back to zero. + (10) A WRITE can only increase the last valid frame pointer in the header. The SQLite core will only ever send requests for UNLOCK, READ, WRITE, diff --git a/src/os.c b/src/os.c index b3e870034..9ad13690c 100644 --- a/src/os.c +++ b/src/os.c @@ -98,6 +98,24 @@ int sqlite3OsSectorSize(sqlite3_file *id){ int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ return id->pMethods->xDeviceCharacteristics(id); } +int sqlite3OsShmOpen(sqlite3_file *id){ + return id->pMethods->xShmOpen(id); +} +int sqlite3OsShmSize(sqlite3_file *id, int reqSize, int *pNewSize){ + return id->pMethods->xShmSize(id, reqSize, pNewSize); +} +int sqlite3OsShmGet(sqlite3_file *id, int reqSize, int *pSize, void **pp){ + return id->pMethods->xShmGet(id, reqSize, pSize, pp); +} +int sqlite3OsShmRelease(sqlite3_file *id){ + return id->pMethods->xShmRelease(id); +} +int sqlite3OsShmLock(sqlite3_file *id, int desiredLock, int *pGotLock){ + return id->pMethods->xShmLock(id, desiredLock, pGotLock); +} +int sqlite3OsShmClose(sqlite3_file *id, int deleteFlag){ + return id->pMethods->xShmClose(id, deleteFlag); +} /* ** The next group of routines are convenience wrappers around the diff --git a/src/os.h b/src/os.h index 7b2bff0dc..cf7f2b7d7 100644 --- a/src/os.h +++ b/src/os.h @@ -243,6 +243,12 @@ int sqlite3OsFileControl(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 int sqlite3OsSectorSize(sqlite3_file *id); int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +int sqlite3OsShmOpen(sqlite3_file *id); +int sqlite3OsShmSize(sqlite3_file *id, int, int*); +int sqlite3OsShmGet(sqlite3_file *id, int, int*, void**); +int sqlite3OsShmRelease(sqlite3_file *id); +int sqlite3OsShmLock(sqlite3_file *id, int, int*); +int sqlite3OsShmClose(sqlite3_file *id, int); /* ** Functions for accessing sqlite3_vfs methods diff --git a/src/os_unix.c b/src/os_unix.c index 50bacfbf7..ff967ca8b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -175,6 +175,9 @@ */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) +/* Forward reference */ +typedef struct unixShm unixShm; +typedef struct unixShmFile unixShmFile; /* ** Sometimes, after a file handle is closed by SQLite, the file descriptor @@ -205,6 +208,8 @@ struct unixFile { void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ int fileFlags; /* Miscellanous flags */ + const char *zPath; /* Name of the file */ + unixShm *pShm; /* Shared memory segment information */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif @@ -3400,1987 +3405,1999 @@ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ return 0; } -/* -** Here ends the implementation of all sqlite3_file methods. -** -********************** End sqlite3_file Methods ******************************* -******************************************************************************/ +#ifndef SQLITE_OMIT_WAL + /* -** This division contains definitions of sqlite3_io_methods objects that -** implement various file locking strategies. It also contains definitions -** of "finder" functions. A finder-function is used to locate the appropriate -** sqlite3_io_methods object for a particular database file. The pAppData -** field of the sqlite3_vfs VFS objects are initialized to be pointers to -** the correct finder-function for that VFS. -** -** Most finder functions return a pointer to a fixed sqlite3_io_methods -** object. The only interesting finder-function is autolockIoFinder, which -** looks at the filesystem type and tries to guess the best locking -** strategy from that. -** -** For finder-funtion F, two objects are created: -** -** (1) The real finder-function named "FImpt()". -** -** (2) A constant pointer to this function named just "F". -** +** Object used to represent a single file opened and mmapped to provide +** shared memory. When multiple threads all reference the same +** log-summary, each thread has its own unixFile object, but they all +** point to a single instance of this object. In other words, each +** log-summary is opened only once per process. ** -** A pointer to the F pointer is used as the pAppData value for VFS -** objects. We have to do this instead of letting pAppData point -** directly at the finder-function since C90 rules prevent a void* -** from be cast into a function pointer. +** unixMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: ** +** nRef +** pNext ** -** Each instance of this macro generates two objects: +** The following fields are read-only after the object is created: +** +** fid +** zFilename ** -** * A constant sqlite3_io_methods object call METHOD that has locking -** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. +** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and +** unixMutexHeld() is true when reading or writing any other field +** in this structure. ** -** * An I/O method finder function called FINDER that returns a pointer -** to the METHOD object in the previous bullet. +** To avoid deadlocks, mutex and mutexBuf are always released in the +** reverse order that they are acquired. mutexBuf is always acquired +** first and released last. This invariant is check by asserting +** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or +** released. */ -#define IOMETHODS(FINDER, METHOD, CLOSE, LOCK, UNLOCK, CKLOCK) \ -static const sqlite3_io_methods METHOD = { \ - 1, /* iVersion */ \ - CLOSE, /* xClose */ \ - unixRead, /* xRead */ \ - unixWrite, /* xWrite */ \ - unixTruncate, /* xTruncate */ \ - unixSync, /* xSync */ \ - unixFileSize, /* xFileSize */ \ - LOCK, /* xLock */ \ - UNLOCK, /* xUnlock */ \ - CKLOCK, /* xCheckReservedLock */ \ - unixFileControl, /* xFileControl */ \ - unixSectorSize, /* xSectorSize */ \ - unixDeviceCharacteristics /* xDeviceCapabilities */ \ -}; \ -static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ - UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ - return &METHOD; \ -} \ -static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ - = FINDER##Impl; +struct unixShmFile { + struct unixFileId fid; /* Unique file identifier */ + sqlite3_mutex *mutex; /* Mutex to access this object */ + sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ + char *zFilename; /* Name of the mmapped file */ + int h; /* Open file descriptor */ + int szMap; /* Size of the mapping of file into memory */ + char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ + int nRef; /* Number of unixShm objects pointing to this */ + unixShm *pFirst; /* All unixShm objects pointing to this */ + unixShmFile *pNext; /* Next in list of all unixShmFile objects */ +#ifdef SQLITE_DEBUG + u8 exclMask; /* Mask of exclusive locks held */ + u8 sharedMask; /* Mask of shared locks held */ + u8 nextShmId; /* Next available unixShm.id value */ +#endif +}; /* -** Here are all of the sqlite3_io_methods objects for each of the -** locking strategies. Functions that return pointers to these methods -** are also created. +** A global array of all unixShmFile objects. +** +** The unixMutexHeld() must be true while reading or writing this list. */ -IOMETHODS( - posixIoFinder, /* Finder function name */ - posixIoMethods, /* sqlite3_io_methods object name */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - unixUnlock, /* xUnlock method */ - unixCheckReservedLock /* xCheckReservedLock method */ -) -IOMETHODS( - nolockIoFinder, /* Finder function name */ - nolockIoMethods, /* sqlite3_io_methods object name */ - nolockClose, /* xClose method */ - nolockLock, /* xLock method */ - nolockUnlock, /* xUnlock method */ - nolockCheckReservedLock /* xCheckReservedLock method */ -) -IOMETHODS( - dotlockIoFinder, /* Finder function name */ - dotlockIoMethods, /* sqlite3_io_methods object name */ - dotlockClose, /* xClose method */ - dotlockLock, /* xLock method */ - dotlockUnlock, /* xUnlock method */ - dotlockCheckReservedLock /* xCheckReservedLock method */ -) +static unixShmFile *unixShmFileList = 0; -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS -IOMETHODS( - flockIoFinder, /* Finder function name */ - flockIoMethods, /* sqlite3_io_methods object name */ - flockClose, /* xClose method */ - flockLock, /* xLock method */ - flockUnlock, /* xUnlock method */ - flockCheckReservedLock /* xCheckReservedLock method */ -) +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** unixShm.pFile->mutex must be held while reading or writing the +** unixShm.pNext and unixShm.locks[] elements. +** +** The unixShm.pFile element is initialized when the object is created +** and is read-only thereafter. +*/ +struct unixShm { + unixShmFile *pFile; /* The underlying unixShmFile object */ + unixShm *pNext; /* Next unixShm with the same unixShmFile */ + u8 lockState; /* Current lock state */ + u8 hasMutex; /* True if holding the unixShmFile mutex */ + u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ + u8 sharedMask; /* Mask of shared locks held */ + u8 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its unixShmFile */ #endif +}; -#if OS_VXWORKS -IOMETHODS( - semIoFinder, /* Finder function name */ - semIoMethods, /* sqlite3_io_methods object name */ - semClose, /* xClose method */ - semLock, /* xLock method */ - semUnlock, /* xUnlock method */ - semCheckReservedLock /* xCheckReservedLock method */ -) -#endif +/* +** Size increment by which shared memory grows +*/ +#define SQLITE_UNIX_SHM_INCR 4096 -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - afpIoFinder, /* Finder function name */ - afpIoMethods, /* sqlite3_io_methods object name */ - afpClose, /* xClose method */ - afpLock, /* xLock method */ - afpUnlock, /* xUnlock method */ - afpCheckReservedLock /* xCheckReservedLock method */ -) -#endif +/* +** Constants used for locking +*/ +#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */ +#define UNIX_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ +#define UNIX_SHM_A 0x10 /* Mask for region locks... */ +#define UNIX_SHM_B 0x20 +#define UNIX_SHM_C 0x40 +#define UNIX_SHM_D 0x80 +#ifdef SQLITE_DEBUG /* -** The proxy locking method is a "super-method" in the sense that it -** opens secondary file descriptors for the conch and lock files and -** it uses proxy, dot-file, AFP, and flock() locking methods on those -** secondary files. For this reason, the division that implements -** proxy locking is located much further down in the file. But we need -** to go ahead and define the sqlite3_io_methods and finder function -** for proxy locking here. So we forward declare the I/O methods. +** Return a pointer to a nul-terminated string in static memory that +** describes a locking mask. The string is of the form "MSABCD" with +** each character representing a lock. "M" for MUTEX, "S" for DMS, +** and "A" through "D" for the region locks. If a lock is held, the +** letter is shown. If the lock is not held, the letter is converted +** to ".". +** +** This routine is for debugging purposes only and does not appear +** in a production build. */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -static int proxyClose(sqlite3_file*); -static int proxyLock(sqlite3_file*, int); -static int proxyUnlock(sqlite3_file*, int); -static int proxyCheckReservedLock(sqlite3_file*, int*); -IOMETHODS( - proxyIoFinder, /* Finder function name */ - proxyIoMethods, /* sqlite3_io_methods object name */ - proxyClose, /* xClose method */ - proxyLock, /* xLock method */ - proxyUnlock, /* xUnlock method */ - proxyCheckReservedLock /* xCheckReservedLock method */ -) -#endif +static const char *unixShmLockString(u8 mask){ + static char zBuf[48]; + static int iBuf = 0; + char *z; -/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -IOMETHODS( - nfsIoFinder, /* Finder function name */ - nfsIoMethods, /* sqlite3_io_methods object name */ - unixClose, /* xClose method */ - unixLock, /* xLock method */ - nfsUnlock, /* xUnlock method */ - unixCheckReservedLock /* xCheckReservedLock method */ -) -#endif + z = &zBuf[iBuf]; + iBuf += 8; + if( iBuf>=sizeof(zBuf) ) iBuf = 0; -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. + z[0] = (mask & UNIX_SHM_DMS) ? 'S' : '.'; + z[1] = (mask & UNIX_SHM_A) ? 'A' : '.'; + z[2] = (mask & UNIX_SHM_B) ? 'B' : '.'; + z[3] = (mask & UNIX_SHM_C) ? 'C' : '.'; + z[4] = (mask & UNIX_SHM_D) ? 'D' : '.'; + z[5] = 0; + return z; +} +#endif /* SQLITE_DEBUG */ + +/* +** Apply posix advisory locks for all bytes identified in lockMask. ** -** This is for MacOSX only. -*/ -static const sqlite3_io_methods *autolockIoFinderImpl( - const char *filePath, /* name of the database file */ - unixFile *pNew /* open file object for the database file */ +** lockMask might contain multiple bits but all bits are guaranteed +** to be contiguous. +** +** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking +** otherwise. +*/ +static int unixShmSystemLock( + unixShmFile *pFile, /* Apply locks to this open shared-memory segment */ + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ + u8 lockMask /* Which bytes to lock or unlock */ ){ - static const struct Mapping { - const char *zFilesystem; /* Filesystem type name */ - const sqlite3_io_methods *pMethods; /* Appropriate locking method */ - } aMap[] = { - { "hfs", &posixIoMethods }, - { "ufs", &posixIoMethods }, - { "afpfs", &afpIoMethods }, - { "smbfs", &afpIoMethods }, - { "webdav", &nolockIoMethods }, - { 0, 0 } - }; - int i; - struct statfs fsInfo; - struct flock lockInfo; + struct flock f; /* The posix advisory locking structure */ + int lockOp; /* The opcode for fcntl() */ + int i; /* Offset into the locking byte range */ + int rc; /* Result code form fcntl() */ + u8 mask; /* Mask of bits in lockMask */ - if( !filePath ){ - /* If filePath==NULL that means we are dealing with a transient file - ** that does not need to be locked. */ - return &nolockIoMethods; + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); + + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + if( lockMask==UNIX_SHM_C && lockType!=F_UNLCK ){ + lockOp = F_SETLKW; + OSTRACE(("SHM-LOCK requesting blocking lock\n")); + }else{ + lockOp = F_SETLK; } - if( statfs(filePath, &fsInfo) != -1 ){ - if( fsInfo.f_flags & MNT_RDONLY ){ - return &nolockIoMethods; - } - for(i=0; aMap[i].zFilesystem; i++){ - if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ - return aMap[i].pMethods; - } - } + + /* Find the first bit in lockMask that is set */ + for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} + assert( mask!=0 ); + f.l_start = i+UNIX_SHM_BASE; + f.l_len = 1; + + /* Extend the locking range for each additional bit that is set */ + mask <<= 1; + while( mask!=0 && (lockMask & mask)!=0 ){ + f.l_len++; + mask <<= 1; } - /* Default case. Handles, amongst others, "nfs". - ** Test byte-range lock using fcntl(). If the call succeeds, - ** assume that the file-system supports POSIX style locks. - */ - lockInfo.l_len = 1; - lockInfo.l_start = 0; - lockInfo.l_whence = SEEK_SET; - lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { - if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ - return &nfsIoMethods; - } else { - return &posixIoMethods; + /* Verify that all bits set in lockMask are contiguous */ + assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + + /* Acquire the system-level lock */ + rc = fcntl(pFile->h, lockOp, &f); + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + + /* Update the global lock state and do debug tracing */ +#ifdef SQLITE_DEBUG + OSTRACE(("SHM-LOCK ")); + if( rc==SQLITE_OK ){ + if( lockType==F_UNLCK ){ + OSTRACE(("unlock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask &= ~lockMask; + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask |= lockMask; + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock ok")); + pFile->exclMask |= lockMask; + pFile->sharedMask &= ~lockMask; } }else{ - return &dotlockIoMethods; + if( lockType==F_UNLCK ){ + OSTRACE(("unlock failed")); + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock failed")); + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock failed")); + } } -} -static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; + OSTRACE((" - change requested %s - afterwards %s:%s\n", + unixShmLockString(lockMask), + unixShmLockString(pFile->sharedMask), + unixShmLockString(pFile->exclMask))); +#endif -#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ + return rc; +} -#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. -** -** This is for VXWorks only. +/* +** For connection p, unlock all of the locks identified by the unlockMask +** parameter. */ -static const sqlite3_io_methods *autolockIoFinderImpl( - const char *filePath, /* name of the database file */ - unixFile *pNew /* the open file object */ +static int unixShmUnlock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to be unlocked */ + u8 unlockMask /* Mask of locks to be unlocked */ ){ - struct flock lockInfo; + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allMask; /* Union of locks held by connections other than "p" */ - if( !filePath ){ - /* If filePath==NULL that means we are dealing with a transient file - ** that does not need to be locked. */ - return &nolockIoMethods; + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); + + /* Compute locks held by sibling connections */ + allMask = 0; + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); + allMask |= pX->sharedMask; } - /* Test if fcntl() is supported and use POSIX style locks. - ** Otherwise fall back to the named semaphore method. - */ - lockInfo.l_len = 1; - lockInfo.l_start = 0; - lockInfo.l_whence = SEEK_SET; - lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { - return &posixIoMethods; + /* Unlock the system-level locks */ + if( (unlockMask & allMask)!=unlockMask ){ + rc = unixShmSystemLock(pFile, F_UNLCK, unlockMask & ~allMask); }else{ - return &semIoMethods; + rc = SQLITE_OK; } -} -static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; -#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~unlockMask; + p->sharedMask &= ~unlockMask; + } + return rc; +} /* -** An abstract type for a pointer to a IO method finder function: +** Get reader locks for connection p on all locks in the readMask parameter. */ -typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); +static int unixShmSharedLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the shared locks */ + u8 readMask /* Mask of shared locks to be acquired */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allShared; /* Union of locks held by connections other than "p" */ + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); -/**************************************************************************** -**************************** sqlite3_vfs methods **************************** -** -** This division contains the implementation of methods on the -** sqlite3_vfs object. -*/ + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + allShared = 0; + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( (~allShared) & readMask ){ + rc = unixShmSystemLock(pFile, F_RDLCK, readMask); + }else{ + rc = SQLITE_OK; + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= readMask; + } + return rc; +} /* -** Initialize the contents of the unixFile structure pointed to by pId. +** For connection p, get an exclusive lock on all locks identified in +** the writeMask parameter. */ -static int fillInUnixFile( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - int h, /* Open file descriptor of file being opened */ - int dirfd, /* Directory file descriptor */ - sqlite3_file *pId, /* Write to the unixFile structure here */ - const char *zFilename, /* Name of the file being opened */ - int noLock, /* Omit locking if true */ - int isDelete /* Delete on close if true */ +static int unixShmExclusiveLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the exclusive locks */ + u8 writeMask /* Mask of exclusive locks to be acquired */ ){ - const sqlite3_io_methods *pLockingStyle; - unixFile *pNew = (unixFile *)pId; - int rc = SQLITE_OK; + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ - assert( pNew->pLock==NULL ); - assert( pNew->pOpen==NULL ); + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); - /* Parameter isDelete is only used on vxworks. Express this explicitly - ** here to prevent compiler warnings about unused parameters. + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. */ - UNUSED_PARAMETER(isDelete); + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; + if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; + } - OSTRACE3("OPEN %-3d %s\n", h, zFilename); - pNew->h = h; - pNew->dirfd = dirfd; - SET_THREADID(pNew); - pNew->fileFlags = 0; + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + rc = unixShmSystemLock(pFile, F_WRLCK, writeMask); + if( rc==SQLITE_OK ){ + p->sharedMask &= ~writeMask; + p->exclMask |= writeMask; + } + return rc; +} -#if OS_VXWORKS - pNew->pId = vxworksFindFileId(zFilename); - if( pNew->pId==0 ){ - noLock = 1; - rc = SQLITE_NOMEM; +/* +** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void unixShmPurge(void){ + unixShmFile **pp; + unixShmFile *p; + assert( unixMutexHeld() ); + pp = &unixShmFileList; + while( (p = *pp)!=0 ){ + if( p->nRef==0 ){ + if( p->mutex ) sqlite3_mutex_free(p->mutex); + if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); + if( p->h>=0 ) close(p->h); + *pp = p->pNext; + sqlite3_free(p); + }else{ + pp = &p->pNext; + } } -#endif +} - if( noLock ){ - pLockingStyle = &nolockIoMethods; - }else{ - pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); -#if SQLITE_ENABLE_LOCKING_STYLE - /* Cache zFilename in the locking context (AFP and dotlock override) for - ** proxyLock activation is possible (remote proxy is based on db name) - ** zFilename remains valid until file is closed, to support */ - pNew->lockingContext = (void*)zFilename; -#endif +/* +** Open a shared-memory area. This particular implementation uses +** mmapped files. +** +** zName is a filename used to identify the shared-memory area. The +** implementation does not (and perhaps should not) use this name +** directly, but rather use it as a template for finding an appropriate +** name for the shared-memory storage. In this implementation, the +** string "-index" is appended to zName and used as the name of the +** mmapped file. +** +** When opening a new shared-memory file, if no other instances of that +** file are currently open, in this process or in other processes, then +** the file must be truncated to zero length or have its header cleared. +*/ +static int unixShmOpen( + sqlite3_file *fd /* The file descriptor of the associated database */ +){ + struct unixShm *p = 0; /* The connection to be opened */ + struct unixShmFile *pFile = 0; /* The underlying mmapped file */ + int rc; /* Result code */ + struct unixFileId fid; /* Unix file identifier */ + struct unixShmFile *pNew; /* Newly allocated pFile */ + struct stat sStat; /* Result from stat() an fstat() */ + struct unixFile *pDbFd; /* Underlying database file */ + int nPath; /* Size of pDbFd->zPath in bytes */ + + /* Allocate space for the new sqlite3_shm object. Also speculatively + ** allocate space for a new unixShmFile and filename. + */ + p = sqlite3_malloc( sizeof(*p) ); + if( p==0 ) return SQLITE_NOMEM; + memset(p, 0, sizeof(*p)); + pDbFd = (struct unixFile*)fd; + assert( pDbFd->pShm==0 ); + nPath = strlen(pDbFd->zPath); + pNew = sqlite3_malloc( sizeof(*pFile) + nPath + 15 ); + if( pNew==0 ){ + sqlite3_free(p); + return SQLITE_NOMEM; } + memset(pNew, 0, sizeof(*pNew)); + pNew->zFilename = (char*)&pNew[1]; + sqlite3_snprintf(nPath+15, pNew->zFilename, "%s-wal-index", pDbFd->zPath); - if( pLockingStyle == &posixIoMethods -#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE - || pLockingStyle == &nfsIoMethods -#endif - ){ - unixEnterMutex(); - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - if( rc!=SQLITE_OK ){ - /* If an error occured in findLockInfo(), close the file descriptor - ** immediately, before releasing the mutex. findLockInfo() may fail - ** in two scenarios: - ** - ** (a) A call to fstat() failed. - ** (b) A malloc failed. - ** - ** Scenario (b) may only occur if the process is holding no other - ** file descriptors open on the same file. If there were other file - ** descriptors on this file, then no malloc would be required by - ** findLockInfo(). If this is the case, it is quite safe to close - ** handle h - as it is guaranteed that no posix locks will be released - ** by doing so. - ** - ** If scenario (a) caused the error then things are not so safe. The - ** implicit assumption here is that if fstat() fails, things are in - ** such bad shape that dropping a lock or two doesn't matter much. - */ - close(h); - h = -1; + /* Look to see if there is an existing unixShmFile that can be used. + ** If no matching unixShmFile currently exists, create a new one. + */ + unixEnterMutex(); + rc = stat(pNew->zFilename, &sStat); + if( rc==0 ){ + memset(&fid, 0, sizeof(fid)); + fid.dev = sStat.st_dev; + fid.ino = sStat.st_ino; + for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){ + if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break; } - unixLeaveMutex(); } + if( pFile ){ + sqlite3_free(pNew); + }else{ + pFile = pNew; + pNew = 0; + pFile->h = -1; + pFile->pNext = unixShmFileList; + unixShmFileList = pFile; -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - else if( pLockingStyle == &afpIoMethods ){ - /* AFP locking uses the file path so it needs to be included in - ** the afpLockingContext. - */ - afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); - if( pCtx==0 ){ + pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutex==0 ){ rc = SQLITE_NOMEM; - }else{ - /* NB: zFilename exists and remains valid until the file is closed - ** according to requirement F11141. So we do not need to make a - ** copy of the filename. */ - pCtx->dbPath = zFilename; - pCtx->reserved = 0; - srandomdev(); - unixEnterMutex(); - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - if( rc!=SQLITE_OK ){ - sqlite3_free(pNew->lockingContext); - close(h); - h = -1; - } - unixLeaveMutex(); + goto shm_open_err; } - } -#endif - - else if( pLockingStyle == &dotlockIoMethods ){ - /* Dotfile locking uses the file path so it needs to be included in - ** the dotlockLockingContext - */ - char *zLockFile; - int nFilename; - nFilename = (int)strlen(zFilename) + 6; - zLockFile = (char *)sqlite3_malloc(nFilename); - if( zLockFile==0 ){ + pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutexBuf==0 ){ rc = SQLITE_NOMEM; - }else{ - sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); + goto shm_open_err; } - pNew->lockingContext = zLockFile; - } -#if OS_VXWORKS - else if( pLockingStyle == &semIoMethods ){ - /* Named semaphore locking uses the file path so it needs to be - ** included in the semLockingContext + pFile->h = open(pFile->zFilename, O_RDWR|O_CREAT, 0664); + if( pFile->h<0 ){ + rc = SQLITE_CANTOPEN_BKPT; + goto shm_open_err; + } + + rc = fstat(pFile->h, &sStat); + if( rc ){ + rc = SQLITE_CANTOPEN_BKPT; + goto shm_open_err; + } + pFile->fid.dev = sStat.st_dev; + pFile->fid.ino = sStat.st_ino; + + /* Check to see if another process is holding the dead-man switch. + ** If not, truncate the file to zero length. */ - unixEnterMutex(); - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ - char *zSemName = pNew->pOpen->aSemName; - int n; - sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", - pNew->pId->zCanonicalName); - for( n=1; zSemName[n]; n++ ) - if( zSemName[n]=='/' ) zSemName[n] = '_'; - pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); - if( pNew->pOpen->pSem == SEM_FAILED ){ - rc = SQLITE_NOMEM; - pNew->pOpen->aSemName[0] = '\0'; + if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){ + if( ftruncate(pFile->h, 0) ){ + rc = SQLITE_IOERR; } } - unixLeaveMutex(); - } -#endif - - pNew->lastErrno = 0; -#if OS_VXWORKS - if( rc!=SQLITE_OK ){ - if( h>=0 ) close(h); - h = -1; - unlink(zFilename); - isDelete = 0; - } - pNew->isDelete = isDelete; -#endif - if( rc!=SQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - if( h>=0 ) close(h); - }else{ - pNew->pMethod = pLockingStyle; - OpenCounter(+1); + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pFile, F_RDLCK, UNIX_SHM_DMS); + } + if( rc ) goto shm_open_err; } - return rc; -} -/* -** Open a file descriptor to the directory containing file zFilename. -** If successful, *pFd is set to the opened file descriptor and -** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM -** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined -** value. -** -** If SQLITE_OK is returned, the caller is responsible for closing -** the file descriptor *pFd using close(). -*/ -static int openDirectory(const char *zFilename, int *pFd){ - int ii; - int fd = -1; - char zDirname[MAX_PATHNAME+1]; - - sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); - for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); - if( ii>0 ){ - zDirname[ii] = '\0'; - fd = open(zDirname, O_RDONLY|O_BINARY, 0); - if( fd>=0 ){ -#ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + /* Make the new connection a child of the unixShmFile */ + p->pFile = pFile; + p->pNext = pFile->pFirst; +#ifdef SQLITE_DEBUG + p->id = pFile->nextShmId++; #endif - OSTRACE3("OPENDIR %-3d %s\n", fd, zDirname); - } - } - *pFd = fd; - return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); + pFile->pFirst = p; + pFile->nRef++; + pDbFd->pShm = p; + unixLeaveMutex(); + return SQLITE_OK; + + /* Jump here on any error */ +shm_open_err: + unixShmPurge(); /* This call frees pFile if required */ + sqlite3_free(p); + sqlite3_free(pNew); + unixLeaveMutex(); + return rc; } /* -** Create a temporary file name in zBuf. zBuf must be allocated -** by the calling process and must be big enough to hold at least -** pVfs->mxPathname bytes. +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. */ -static int getTempname(int nBuf, char *zBuf){ - static const char *azDirs[] = { - 0, - 0, - "/var/tmp", - "/usr/tmp", - "/tmp", - ".", - }; - static const unsigned char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - unsigned int i, j; - struct stat buf; - const char *zDir = "."; +static int unixShmClose( + sqlite3_file *fd, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + unixShm *p; /* The connection to be closed */ + unixShmFile *pFile; /* The underlying shared-memory file */ + unixShm **pp; /* For looping over sibling connections */ + unixFile *pDbFd; /* The underlying database file */ - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. - */ - SimulateIOError( return SQLITE_IOERR ); + pDbFd = (unixFile*)fd; + p = pDbFd->pShm; + if( p==0 ) return SQLITE_OK; + pFile = p->pFile; - azDirs[0] = sqlite3_temp_directory; - if (NULL == azDirs[1]) { - azDirs[1] = getenv("TMPDIR"); - } - - for(i=0; iexclMask==0 ); + assert( p->sharedMask==0 ); - /* Check that the output buffer is large enough for the temporary file - ** name. If it is not, return SQLITE_ERROR. - */ - if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){ - return SQLITE_ERROR; + /* Remove connection p from the set of connections associated with pFile */ + sqlite3_mutex_enter(pFile->mutex); + for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} + *pp = p->pNext; + + /* Free the connection p */ + sqlite3_free(p); + pDbFd->pShm = 0; + sqlite3_mutex_leave(pFile->mutex); + + /* If pFile->nRef has reached 0, then close the underlying + ** shared-memory file, too */ + unixEnterMutex(); + assert( pFile->nRef>0 ); + pFile->nRef--; + if( pFile->nRef==0 ){ + if( deleteFlag ) unlink(pFile->zFilename); + unixShmPurge(); } + unixLeaveMutex(); - do{ - sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); - j = (int)strlen(zBuf); - sqlite3_randomness(15, &zBuf[j]); - for(i=0; i<15; i++, j++){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; - } - zBuf[j] = 0; - }while( access(zBuf,0)==0 ); return SQLITE_OK; } -#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) -/* -** Routine to transform a unixFile into a proxy-locking unixFile. -** Implementation in the proxy-lock division, but used by unixOpen() -** if SQLITE_PREFER_PROXY_LOCKING is defined. -*/ -static int proxyTransformUnixFile(unixFile*, const char*); -#endif - /* -** Search for an unused file descriptor that was opened on the database -** file (not a journal or master-journal file) identified by pathname -** zPath with SQLITE_OPEN_XXX flags matching those passed as the second -** argument to this function. +** Query and/or changes the size of the underlying storage for +** a shared-memory segment. The reqSize parameter is the new size +** of the underlying storage, or -1 to do just a query. The size +** of the underlying storage (after resizing if resizing occurs) is +** written into pNewSize. ** -** Such a file descriptor may exist if a database connection was closed -** but the associated file descriptor could not be closed because some -** other file descriptor open on the same file is holding a file-lock. -** Refer to comments in the unixClose() function and the lengthy comment -** describing "Posix Advisory Locking" at the start of this file for -** further details. Also, ticket #4018. +** This routine does not (necessarily) change the size of the mapping +** of the underlying storage into memory. Use xShmGet() to change +** the mapping size. ** -** If a suitable file descriptor is found, then it is returned. If no -** such file descriptor is located, -1 is returned. +** The reqSize parameter is the minimum size requested. The implementation +** is free to expand the storage to some larger amount if it chooses. */ -static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ - UnixUnusedFd *pUnused = 0; - - /* Do not search for an unused file descriptor on vxworks. Not because - ** vxworks would not benefit from the change (it might, we're not sure), - ** but because no way to test it is currently available. It is better - ** not to risk breaking vxworks support for the sake of such an obscure - ** feature. */ -#if !OS_VXWORKS - struct stat sStat; /* Results of stat() call */ - - /* A stat() call may fail for various reasons. If this happens, it is - ** almost certain that an open() call on the same path will also fail. - ** For this reason, if an error occurs in the stat() call here, it is - ** ignored and -1 is returned. The caller will try to open a new file - ** descriptor on the same path, fail, and return an error to SQLite. - ** - ** Even if a subsequent open() call does succeed, the consequences of - ** not searching for a resusable file descriptor are not dire. */ - if( 0==stat(zPath, &sStat) ){ - struct unixOpenCnt *pOpen; +static int unixShmSize( + sqlite3_file *fd, /* The open database file holding SHM */ + int reqSize, /* Requested size. -1 for query only */ + int *pNewSize /* Write new size here */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_OK; + struct stat sStat; - unixEnterMutex(); - pOpen = openList; - while( pOpen && (pOpen->fileId.dev!=sStat.st_dev - || pOpen->fileId.ino!=sStat.st_ino) ){ - pOpen = pOpen->pNext; - } - if( pOpen ){ - UnixUnusedFd **pp; - for(pp=&pOpen->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); - pUnused = *pp; - if( pUnused ){ - *pp = pUnused->pNext; - } - } - unixLeaveMutex(); + if( reqSize>=0 ){ + reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; + reqSize *= SQLITE_UNIX_SHM_INCR; + rc = ftruncate(pFile->h, reqSize); } -#endif /* if !OS_VXWORKS */ - return pUnused; + if( fstat(pFile->h, &sStat)==0 ){ + *pNewSize = (int)sStat.st_size; + }else{ + *pNewSize = 0; + rc = SQLITE_IOERR; + } + return rc; } + /* -** Open the file zPath. -** -** Previously, the SQLite OS layer used three functions in place of this -** one: +** Map the shared storage into memory. The minimum size of the +** mapping should be reqMapSize if reqMapSize is positive. If +** reqMapSize is zero or negative, the implementation can choose +** whatever mapping size is convenient. ** -** sqlite3OsOpenReadWrite(); -** sqlite3OsOpenReadOnly(); -** sqlite3OsOpenExclusive(); +** *ppBuf is made to point to the memory which is a mapping of the +** underlying storage. A mutex is acquired to prevent other threads +** from running while *ppBuf is in use in order to prevent other threads +** remapping *ppBuf out from under this thread. The unixShmRelease() +** call will release the mutex. However, if the lock state is CHECKPOINT, +** the mutex is not acquired because CHECKPOINT will never remap the +** buffer. RECOVER might remap, though, so CHECKPOINT will acquire +** the mutex if and when it promotes to RECOVER. ** -** These calls correspond to the following combinations of flags: +** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from +** being remapped also prevents more than one thread from being in +** RECOVER at a time. But, RECOVER sometimes wants to remap itself. +** To prevent RECOVER from losing its lock while remapping, the +** mutex is not released by unixShmRelease() when in RECOVER. ** -** ReadWrite() -> (READWRITE | CREATE) -** ReadOnly() -> (READONLY) -** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) +** *pNewMapSize is set to the size of the mapping. ** -** The old OpenExclusive() accepted a boolean argument - "delFlag". If -** true, the file was configured to be automatically deleted when the -** file handle closed. To achieve the same effect using this new -** interface, add the DELETEONCLOSE flag to those specified above for -** OpenExclusive(). +** *ppBuf and *pNewMapSize might be NULL and zero if no space has +** yet been allocated to the underlying storage. */ -static int unixOpen( - sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ - const char *zPath, /* Pathname of file to be opened */ - sqlite3_file *pFile, /* The file descriptor to be filled in */ - int flags, /* Input flags to control the opening */ - int *pOutFlags /* Output flags returned to SQLite core */ +static int unixShmGet( + sqlite3_file *fd, /* Database file holding shared memory */ + int reqMapSize, /* Requested size of mapping. -1 means don't care */ + int *pNewMapSize, /* Write new size of mapping here */ + void **ppBuf /* Write mapping buffer origin here */ ){ - unixFile *p = (unixFile *)pFile; - int fd = -1; /* File descriptor returned by open() */ - int dirfd = -1; /* Directory file descriptor */ - int openFlags = 0; /* Flags to pass to open() */ - int eType = flags&0xFFFFFF00; /* Type of file to open */ - int noLock; /* True to omit locking primitives */ - int rc = SQLITE_OK; /* Function Return Code */ - - int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); - int isCreate = (flags & SQLITE_OPEN_CREATE); - int isReadonly = (flags & SQLITE_OPEN_READONLY); - int isReadWrite = (flags & SQLITE_OPEN_READWRITE); -#if SQLITE_ENABLE_LOCKING_STYLE - int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); -#endif - - /* If creating a master or main-file journal, this function will open - ** a file-descriptor on the directory too. The first time unixSync() - ** is called the directory file descriptor will be fsync()ed and close()d. - */ - int isOpenDirectory = (isCreate && - (eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL) - ); - - /* If argument zPath is a NULL pointer, this function is required to open - ** a temporary file. Use this buffer to store the file name in. - */ - char zTmpname[MAX_PATHNAME+1]; - const char *zName = zPath; - - /* Check the following statements are true: - ** - ** (a) Exactly one of the READWRITE and READONLY flags must be set, and - ** (b) if CREATE is set, then READWRITE must also be set, and - ** (c) if EXCLUSIVE is set, then CREATE must also be set. - ** (d) if DELETEONCLOSE is set, then CREATE must also be set. - */ - assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); - assert(isCreate==0 || isReadWrite); - assert(isExclusive==0 || isCreate); - assert(isDelete==0 || isCreate); - - /* The main DB, main journal, and master journal are never automatically - ** deleted. Nor are they ever temporary files. */ - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); - - /* Assert that the upper layer has set one of the "file-type" flags. */ - assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB - || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL - || eType==SQLITE_OPEN_TRANSIENT_DB - ); - - memset(p, 0, sizeof(unixFile)); - - if( eType==SQLITE_OPEN_MAIN_DB ){ - UnixUnusedFd *pUnused; - pUnused = findReusableFd(zName, flags); - if( pUnused ){ - fd = pUnused->fd; - }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); - if( !pUnused ){ - return SQLITE_NOMEM; - } - } - p->pUnused = pUnused; - }else if( !zName ){ - /* If zName is NULL, the upper layer is requesting a temp file. */ - assert(isDelete && !isOpenDirectory); - rc = getTempname(MAX_PATHNAME+1, zTmpname); - if( rc!=SQLITE_OK ){ - return rc; - } - zName = zTmpname; - } - - /* Determine the value of the flags parameter passed to POSIX function - ** open(). These must be calculated even if open() is not called, as - ** they may be stored as part of the file handle and used by the - ** 'conch file' locking functions later on. */ - if( isReadonly ) openFlags |= O_RDONLY; - if( isReadWrite ) openFlags |= O_RDWR; - if( isCreate ) openFlags |= O_CREAT; - if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); - openFlags |= (O_LARGEFILE|O_BINARY); - - if( fd<0 ){ - mode_t openMode = (isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); - fd = open(zName, openFlags, openMode); - OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); - if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ - /* Failed to open the file for read/write access. Try read-only. */ - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - openFlags &= ~(O_RDWR|O_CREAT); - flags |= SQLITE_OPEN_READONLY; - openFlags |= O_RDONLY; - fd = open(zName, openFlags, openMode); - } - if( fd<0 ){ - rc = SQLITE_CANTOPEN_BKPT; - goto open_finished; - } - } - assert( fd>=0 ); - if( pOutFlags ){ - *pOutFlags = flags; - } - - if( p->pUnused ){ - p->pUnused->fd = fd; - p->pUnused->flags = flags; - } - - if( isDelete ){ -#if OS_VXWORKS - zPath = zName; -#else - unlink(zName); -#endif - } -#if SQLITE_ENABLE_LOCKING_STYLE - else{ - p->openFlags = openFlags; - } -#endif - - if( isOpenDirectory ){ - rc = openDirectory(zPath, &dirfd); - if( rc!=SQLITE_OK ){ - /* It is safe to close fd at this point, because it is guaranteed not - ** to be open on a database file. If it were open on a database file, - ** it would not be safe to close as this would release any locks held - ** on the file by this process. */ - assert( eType!=SQLITE_OPEN_MAIN_DB ); - close(fd); /* silently leak if fail, already in error */ - goto open_finished; - } - } - -#ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); -#endif - - noLock = eType!=SQLITE_OPEN_MAIN_DB; - - -#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE - struct statfs fsInfo; - if( fstatfs(fd, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ - close(fd); /* silently leak if fail, in error */ - return SQLITE_IOERR_ACCESS; - } - if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { - ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; - } -#endif - -#if SQLITE_ENABLE_LOCKING_STYLE -#if SQLITE_PREFER_PROXY_LOCKING - isAutoProxy = 1; -#endif - if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ - char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); - int useProxy = 0; + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_OK; - /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means - ** never use proxy, NULL means use proxy for non-local files only. */ - if( envforce!=NULL ){ - useProxy = atoi(envforce)>0; - }else{ - struct statfs fsInfo; - if( statfs(zPath, &fsInfo) == -1 ){ - /* In theory, the close(fd) call is sub-optimal. If the file opened - ** with fd is a database file, and there are other connections open - ** on that file that are currently holding advisory locks on it, - ** then the call to close() will cancel those locks. In practice, - ** we're assuming that statfs() doesn't fail very often. At least - ** not while other file descriptors opened by the same process on - ** the same file are working. */ - p->lastErrno = errno; - if( dirfd>=0 ){ - close(dirfd); /* silently leak if fail, in error */ - } - close(fd); /* silently leak if fail, in error */ - rc = SQLITE_IOERR_ACCESS; - goto open_finished; - } - useProxy = !(fsInfo.f_flags&MNT_LOCAL); - } - if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); - if( rc==SQLITE_OK ){ - rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); - if( rc!=SQLITE_OK ){ - /* Use unixClose to clean up the resources added in fillInUnixFile - ** and clear all the structure's references. Specifically, - ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op - */ - unixClose(pFile); - return rc; - } - } - goto open_finished; - } - } -#endif - - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); -open_finished: - if( rc!=SQLITE_OK ){ - sqlite3_free(p->pUnused); + if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){ + assert( sqlite3_mutex_notheld(pFile->mutex) ); + sqlite3_mutex_enter(pFile->mutexBuf); + p->hasMutexBuf = 1; } - return rc; -} - - -/* -** Delete the file at zPath. If the dirSync argument is true, fsync() -** the directory after deleting the file. -*/ -static int unixDelete( - sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ - const char *zPath, /* Name of file to be deleted */ - int dirSync /* If true, fsync() directory after deleting file */ -){ - int rc = SQLITE_OK; - UNUSED_PARAMETER(NotUsed); - SimulateIOError(return SQLITE_IOERR_DELETE); - unlink(zPath); -#ifndef SQLITE_DISABLE_DIRSYNC - if( dirSync ){ - int fd; - rc = openDirectory(zPath, &fd); - if( rc==SQLITE_OK ){ -#if OS_VXWORKS - if( fsync(fd)==-1 ) -#else - if( fsync(fd) ) -#endif - { - rc = SQLITE_IOERR_DIR_FSYNC; - } - if( close(fd)&&!rc ){ - rc = SQLITE_IOERR_DIR_CLOSE; - } + sqlite3_mutex_enter(pFile->mutex); + if( pFile->szMap==0 || reqMapSize>pFile->szMap ){ + int actualSize; + if( unixShmSize(fd, -1, &actualSize)==SQLITE_OK + && reqMapSizepMMapBuf ){ + munmap(pFile->pMMapBuf, pFile->szMap); + } + pFile->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, + pFile->h, 0); + pFile->szMap = pFile->pMMapBuf ? reqMapSize : 0; } -#endif - return rc; -} - -/* -** Test the existance of or access permissions of file zPath. The -** test performed depends on the value of flags: -** -** SQLITE_ACCESS_EXISTS: Return 1 if the file exists -** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. -** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. -** -** Otherwise return 0. -*/ -static int unixAccess( - sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ - const char *zPath, /* Path of the file to examine */ - int flags, /* What do we want to learn about the zPath file? */ - int *pResOut /* Write result boolean here */ -){ - int amode = 0; - UNUSED_PARAMETER(NotUsed); - SimulateIOError( return SQLITE_IOERR_ACCESS; ); - switch( flags ){ - case SQLITE_ACCESS_EXISTS: - amode = F_OK; - break; - case SQLITE_ACCESS_READWRITE: - amode = W_OK|R_OK; - break; - case SQLITE_ACCESS_READ: - amode = R_OK; - break; - - default: - assert(!"Invalid flags argument"); - } - *pResOut = (access(zPath, amode)==0); - return SQLITE_OK; + *pNewMapSize = pFile->szMap; + *ppBuf = pFile->pMMapBuf; + sqlite3_mutex_leave(pFile->mutex); + return rc; } - /* -** Turn a relative pathname into a full pathname. The relative path -** is stored as a nul-terminated string in the buffer pointed to by -** zPath. +** Release the lock held on the shared memory segment to that other +** threads are free to resize it if necessary. ** -** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes -** (in this case, MAX_PATHNAME bytes). The full-path is written to -** this buffer before returning. +** If the lock is not currently held, this routine is a harmless no-op. +** +** If the shared-memory object is in lock state RECOVER, then we do not +** really want to release the lock, so in that case too, this routine +** is a no-op. */ -static int unixFullPathname( - sqlite3_vfs *pVfs, /* Pointer to vfs object */ - const char *zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char *zOut /* Output buffer */ -){ - - /* It's odd to simulate an io-error here, but really this is just - ** using the io-error infrastructure to test that SQLite handles this - ** function failing. This function could fail if, for example, the - ** current working directory has been unlinked. - */ - SimulateIOError( return SQLITE_ERROR ); - - assert( pVfs->mxPathname==MAX_PATHNAME ); - UNUSED_PARAMETER(pVfs); +static int unixShmRelease(sqlite3_file *fd){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; - zOut[nOut-1] = '\0'; - if( zPath[0]=='/' ){ - sqlite3_snprintf(nOut, zOut, "%s", zPath); - }else{ - int nCwd; - if( getcwd(zOut, nOut-1)==0 ){ - return SQLITE_CANTOPEN_BKPT; - } - nCwd = (int)strlen(zOut); - sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); + if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){ + assert( sqlite3_mutex_notheld(p->pFile->mutex) ); + sqlite3_mutex_leave(p->pFile->mutexBuf); + p->hasMutexBuf = 0; } return SQLITE_OK; } - -#ifndef SQLITE_OMIT_LOAD_EXTENSION -/* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. -*/ -#include -static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){ - UNUSED_PARAMETER(NotUsed); - return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); -} - /* -** SQLite calls this function immediately after a call to unixDlSym() or -** unixDlOpen() fails (returns a null pointer). If a more detailed error -** message is available, it is written to zBufOut. If no error message -** is available, zBufOut is left unmodified and SQLite uses a default -** error message. +** Symbolic names for LOCK states used for debugging. */ -static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ - char *zErr; - UNUSED_PARAMETER(NotUsed); - unixEnterMutex(); - zErr = dlerror(); - if( zErr ){ - sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); - } - unixLeaveMutex(); -} -static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ - /* - ** GCC with -pedantic-errors says that C90 does not allow a void* to be - ** cast into a pointer to a function. And yet the library dlsym() routine - ** returns a void* which is really a pointer to a function. So how do we - ** use dlsym() with -pedantic-errors? - ** - ** Variable x below is defined to be a pointer to a function taking - ** parameters void* and const char* and returning a pointer to a function. - ** We initialize x by assigning it a pointer to the dlsym() function. - ** (That assignment requires a cast.) Then we call the function that - ** x points to. - ** - ** This work-around is unlikely to work correctly on any system where - ** you really cannot cast a function pointer into void*. But then, on the - ** other hand, dlsym() will not work on such a system either, so we have - ** not really lost anything. - */ - void (*(*x)(void*,const char*))(void); - UNUSED_PARAMETER(NotUsed); - x = (void(*(*)(void*,const char*))(void))dlsym; - return (*x)(p, zSym); -} -static void unixDlClose(sqlite3_vfs *NotUsed, void *pHandle){ - UNUSED_PARAMETER(NotUsed); - dlclose(pHandle); -} -#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define unixDlOpen 0 - #define unixDlError 0 - #define unixDlSym 0 - #define unixDlClose 0 +#ifdef SQLITE_DEBUG +static const char *azLkName[] = { + "UNLOCK", + "READ", + "READ_FULL", + "WRITE", + "PENDING", + "CHECKPOINT", + "RECOVER" +}; #endif + /* -** Write nBuf bytes of random data to the supplied buffer zBuf. +** Change the lock state for a shared-memory segment. */ -static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ - UNUSED_PARAMETER(NotUsed); - assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); +static int unixShmLock( + sqlite3_file *fd, /* Database file holding the shared memory */ + int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ + int *pGotLock /* The lock you actually got */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_PROTOCOL; - /* We have to initialize zBuf to prevent valgrind from reporting - ** errors. The reports issued by valgrind are incorrect - we would - ** prefer that the randomness be increased by making use of the - ** uninitialized space in zBuf - but valgrind errors tend to worry - ** some users. Rather than argue, it seems easier just to initialize - ** the whole array and silence valgrind, even if that means less randomness - ** in the random seed. - ** - ** When testing, initializing zBuf[] to zero is all we do. That means - ** that we always use the same random number sequence. This makes the - ** tests repeatable. + /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never + ** directly requested; they are side effects from requesting + ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. */ - memset(zBuf, 0, nBuf); -#if !defined(SQLITE_TEST) - { - int pid, fd; - fd = open("/dev/urandom", O_RDONLY); - if( fd<0 ){ - time_t t; - time(&t); - memcpy(zBuf, &t, sizeof(t)); - pid = getpid(); - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); - assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); - nBuf = sizeof(t) + sizeof(pid); - }else{ - nBuf = read(fd, zBuf, nBuf); - close(fd); + assert( desiredLock==SQLITE_SHM_UNLOCK + || desiredLock==SQLITE_SHM_READ + || desiredLock==SQLITE_SHM_WRITE + || desiredLock==SQLITE_SHM_CHECKPOINT + || desiredLock==SQLITE_SHM_RECOVER ); + + /* Return directly if this is just a lock state query, or if + ** the connection is already in the desired locking state. + */ + if( desiredLock==p->lockState + || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) + ){ + OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n", + p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState])); + if( pGotLock ) *pGotLock = p->lockState; + return SQLITE_OK; + } + + OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n", + p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock])); + + if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ + assert( sqlite3_mutex_notheld(pFile->mutex) ); + sqlite3_mutex_enter(pFile->mutexBuf); + p->hasMutexBuf = 1; + } + sqlite3_mutex_enter(pFile->mutex); + switch( desiredLock ){ + case SQLITE_SHM_UNLOCK: { + assert( p->lockState!=SQLITE_SHM_RECOVER ); + unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D); + rc = SQLITE_OK; + p->lockState = SQLITE_SHM_UNLOCK; + break; + } + case SQLITE_SHM_READ: { + if( p->lockState==SQLITE_SHM_UNLOCK ){ + int nAttempt; + rc = SQLITE_BUSY; + assert( p->lockState==SQLITE_SHM_UNLOCK ); + for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B); + if( rc==SQLITE_BUSY ){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_READ_FULL; + } + }else{ + unixShmUnlock(pFile, p, UNIX_SHM_B); + p->lockState = SQLITE_SHM_READ; + } + } + }else{ + assert( p->lockState==SQLITE_SHM_WRITE + || p->lockState==SQLITE_SHM_RECOVER ); + rc = unixShmSharedLock(pFile, p, UNIX_SHM_A); + unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + p->lockState = SQLITE_SHM_READ; + } + break; + } + case SQLITE_SHM_WRITE: { + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL ); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_WRITE; + } + break; + } + case SQLITE_SHM_CHECKPOINT: { + assert( p->lockState==SQLITE_SHM_UNLOCK + || p->lockState==SQLITE_SHM_PENDING + ); + if( p->lockState==SQLITE_SHM_UNLOCK ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_PENDING; + } + } + if( p->lockState==SQLITE_SHM_PENDING ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_CHECKPOINT; + } + } + break; + } + default: { + assert( desiredLock==SQLITE_SHM_RECOVER ); + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL + ); + assert( sqlite3_mutex_held(pFile->mutexBuf) ); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_RECOVER; + } + break; } } -#endif - return nBuf; + sqlite3_mutex_leave(pFile->mutex); + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n", + p->id, getpid(), azLkName[p->lockState])); + if( pGotLock ) *pGotLock = p->lockState; + return rc; } - -/* -** Sleep for a little while. Return the amount of time slept. -** The argument is the number of microseconds we want to sleep. -** The return value is the number of microseconds of sleep actually -** requested from the underlying operating system, a number which -** might be greater than or equal to the argument, but not less -** than the argument. -*/ -static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ -#if OS_VXWORKS - struct timespec sp; - - sp.tv_sec = microseconds / 1000000; - sp.tv_nsec = (microseconds % 1000000) * 1000; - nanosleep(&sp, NULL); - UNUSED_PARAMETER(NotUsed); - return microseconds; -#elif defined(HAVE_USLEEP) && HAVE_USLEEP - usleep(microseconds); - UNUSED_PARAMETER(NotUsed); - return microseconds; #else - int seconds = (microseconds+999999)/1000000; - sleep(seconds); - UNUSED_PARAMETER(NotUsed); - return seconds*1000000; -#endif -} - -/* -** The following variable, if set to a non-zero value, is interpreted as -** the number of seconds since 1970 and is used to set the result of -** sqlite3OsCurrentTime() during testing. -*/ -#ifdef SQLITE_TEST -int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ -#endif +# define unixShmOpen 0 +# define unixShmSize 0 +# define unixShmGet 0 +# define unixShmRelease 0 +# define unixShmLock 0 +# define unixShmClose 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ /* -** Find the current time (in Universal Coordinated Time). Write into *piNow -** the current time and date as a Julian Day number times 86_400_000. In -** other words, write into *piNow the number of milliseconds since the Julian -** epoch of noon in Greenwich on November 24, 4714 B.C according to the -** proleptic Gregorian calendar. +** Here ends the implementation of all sqlite3_file methods. ** -** On success, return 0. Return 1 if the time and date cannot be found. -*/ -static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ - static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; -#if defined(NO_GETTOD) - time_t t; - time(&t); - *piNow = ((sqlite3_int64)i)*1000 + unixEpoch; -#elif OS_VXWORKS - struct timespec sNow; - clock_gettime(CLOCK_REALTIME, &sNow); - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; -#else - struct timeval sNow; - gettimeofday(&sNow, 0); - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; -#endif - -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; - } -#endif - UNUSED_PARAMETER(NotUsed); - return 0; -} - -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ - sqlite3_int64 i; - unixCurrentTimeInt64(0, &i); - *prNow = i/86400000.0; - return 0; -} - -/* -** We added the xGetLastError() method with the intention of providing -** better low-level error messages when operating-system problems come up -** during SQLite operation. But so far, none of that has been implemented -** in the core. So this routine is never called. For now, it is merely -** a place-holder. -*/ -static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ - UNUSED_PARAMETER(NotUsed); - UNUSED_PARAMETER(NotUsed2); - UNUSED_PARAMETER(NotUsed3); - return 0; -} - -#ifndef SQLITE_OMIT_WAL - -/* Forward reference */ -typedef struct unixShm unixShm; -typedef struct unixShmFile unixShmFile; +********************** End sqlite3_file Methods ******************************* +******************************************************************************/ /* -** Object used to represent a single file opened and mmapped to provide -** shared memory. When multiple threads all reference the same -** log-summary, each thread has its own unixFile object, but they all -** point to a single instance of this object. In other words, each -** log-summary is opened only once per process. +** This division contains definitions of sqlite3_io_methods objects that +** implement various file locking strategies. It also contains definitions +** of "finder" functions. A finder-function is used to locate the appropriate +** sqlite3_io_methods object for a particular database file. The pAppData +** field of the sqlite3_vfs VFS objects are initialized to be pointers to +** the correct finder-function for that VFS. ** -** unixMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: +** Most finder functions return a pointer to a fixed sqlite3_io_methods +** object. The only interesting finder-function is autolockIoFinder, which +** looks at the filesystem type and tries to guess the best locking +** strategy from that. ** -** nRef -** pNext +** For finder-funtion F, two objects are created: ** -** The following fields are read-only after the object is created: -** -** fid -** zFilename +** (1) The real finder-function named "FImpt()". ** -** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and -** unixMutexHeld() is true when reading or writing any other field -** in this structure. +** (2) A constant pointer to this function named just "F". ** -** To avoid deadlocks, mutex and mutexBuf are always released in the -** reverse order that they are acquired. mutexBuf is always acquired -** first and released last. This invariant is check by asserting -** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or -** released. +** +** A pointer to the F pointer is used as the pAppData value for VFS +** objects. We have to do this instead of letting pAppData point +** directly at the finder-function since C90 rules prevent a void* +** from be cast into a function pointer. +** +** +** Each instance of this macro generates two objects: +** +** * A constant sqlite3_io_methods object call METHOD that has locking +** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. +** +** * An I/O method finder function called FINDER that returns a pointer +** to the METHOD object in the previous bullet. */ -struct unixShmFile { - struct unixFileId fid; /* Unique file identifier */ - sqlite3_mutex *mutex; /* Mutex to access this object */ - sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ - char *zFilename; /* Name of the file */ - int h; /* Open file descriptor */ - int szMap; /* Size of the mapping of file into memory */ - char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ - int nRef; /* Number of unixShm objects pointing to this */ - unixShm *pFirst; /* All unixShm objects pointing to this */ - unixShmFile *pNext; /* Next in list of all unixShmFile objects */ -#ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available unixShm.id value */ +#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \ +static const sqlite3_io_methods METHOD = { \ + VERSION, /* iVersion */ \ + CLOSE, /* xClose */ \ + unixRead, /* xRead */ \ + unixWrite, /* xWrite */ \ + unixTruncate, /* xTruncate */ \ + unixSync, /* xSync */ \ + unixFileSize, /* xFileSize */ \ + LOCK, /* xLock */ \ + UNLOCK, /* xUnlock */ \ + CKLOCK, /* xCheckReservedLock */ \ + unixFileControl, /* xFileControl */ \ + unixSectorSize, /* xSectorSize */ \ + unixDeviceCharacteristics, /* xDeviceCapabilities */ \ + unixShmOpen, /* xShmOpen */ \ + unixShmSize, /* xShmSize */ \ + unixShmGet, /* xShmGet */ \ + unixShmRelease, /* xShmRelease */ \ + unixShmLock, /* xShmLock */ \ + unixShmClose /* xShmClose */ \ +}; \ +static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ + UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ + return &METHOD; \ +} \ +static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ + = FINDER##Impl; + +/* +** Here are all of the sqlite3_io_methods objects for each of the +** locking strategies. Functions that return pointers to these methods +** are also created. +*/ +IOMETHODS( + posixIoFinder, /* Finder function name */ + posixIoMethods, /* sqlite3_io_methods object name */ + 2, /* ShmOpen is enabled */ + unixClose, /* xClose method */ + unixLock, /* xLock method */ + unixUnlock, /* xUnlock method */ + unixCheckReservedLock /* xCheckReservedLock method */ +) +IOMETHODS( + nolockIoFinder, /* Finder function name */ + nolockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + nolockClose, /* xClose method */ + nolockLock, /* xLock method */ + nolockUnlock, /* xUnlock method */ + nolockCheckReservedLock /* xCheckReservedLock method */ +) +IOMETHODS( + dotlockIoFinder, /* Finder function name */ + dotlockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + dotlockClose, /* xClose method */ + dotlockLock, /* xLock method */ + dotlockUnlock, /* xUnlock method */ + dotlockCheckReservedLock /* xCheckReservedLock method */ +) + +#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +IOMETHODS( + flockIoFinder, /* Finder function name */ + flockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + flockClose, /* xClose method */ + flockLock, /* xLock method */ + flockUnlock, /* xUnlock method */ + flockCheckReservedLock /* xCheckReservedLock method */ +) +#endif + +#if OS_VXWORKS +IOMETHODS( + semIoFinder, /* Finder function name */ + semIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + semClose, /* xClose method */ + semLock, /* xLock method */ + semUnlock, /* xUnlock method */ + semCheckReservedLock /* xCheckReservedLock method */ +) #endif -}; -/* -** A global array of all unixShmFile objects. -** -** The unixMutexHeld() must be true while reading or writing this list. -*/ -static unixShmFile *unixShmFileList = 0; +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + afpIoFinder, /* Finder function name */ + afpIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + afpClose, /* xClose method */ + afpLock, /* xLock method */ + afpUnlock, /* xUnlock method */ + afpCheckReservedLock /* xCheckReservedLock method */ +) +#endif /* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** unixShm.pFile->mutex must be held while reading or writing the -** unixShm.pNext and unixShm.locks[] elements. -** -** The unixShm.pFile element is initialized when the object is created -** and is read-only thereafter. +** The proxy locking method is a "super-method" in the sense that it +** opens secondary file descriptors for the conch and lock files and +** it uses proxy, dot-file, AFP, and flock() locking methods on those +** secondary files. For this reason, the division that implements +** proxy locking is located much further down in the file. But we need +** to go ahead and define the sqlite3_io_methods and finder function +** for proxy locking here. So we forward declare the I/O methods. */ -struct unixShm { - unixShmFile *pFile; /* The underlying unixShmFile object */ - unixShm *pNext; /* Next unixShm with the same unixShmFile */ - u8 lockState; /* Current lock state */ - u8 hasMutex; /* True if holding the unixShmFile mutex */ - u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ - u8 sharedMask; /* Mask of shared locks held */ - u8 exclMask; /* Mask of exclusive locks held */ -#ifdef SQLITE_DEBUG - u8 id; /* Id of this connection with its unixShmFile */ +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +static int proxyClose(sqlite3_file*); +static int proxyLock(sqlite3_file*, int); +static int proxyUnlock(sqlite3_file*, int); +static int proxyCheckReservedLock(sqlite3_file*, int*); +IOMETHODS( + proxyIoFinder, /* Finder function name */ + proxyIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + proxyClose, /* xClose method */ + proxyLock, /* xLock method */ + proxyUnlock, /* xUnlock method */ + proxyCheckReservedLock /* xCheckReservedLock method */ +) #endif -}; -/* -** Size increment by which shared memory grows -*/ -#define SQLITE_UNIX_SHM_INCR 4096 +/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + nfsIoFinder, /* Finder function name */ + nfsIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ + unixClose, /* xClose method */ + unixLock, /* xLock method */ + nfsUnlock, /* xUnlock method */ + unixCheckReservedLock /* xCheckReservedLock method */ +) +#endif -/* -** Constants used for locking +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE +/* +** This "finder" function attempts to determine the best locking strategy +** for the database file "filePath". It then returns the sqlite3_io_methods +** object that implements that strategy. +** +** This is for MacOSX only. */ -#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */ -#define UNIX_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ -#define UNIX_SHM_A 0x10 /* Mask for region locks... */ -#define UNIX_SHM_B 0x20 -#define UNIX_SHM_C 0x40 -#define UNIX_SHM_D 0x80 +static const sqlite3_io_methods *autolockIoFinderImpl( + const char *filePath, /* name of the database file */ + unixFile *pNew /* open file object for the database file */ +){ + static const struct Mapping { + const char *zFilesystem; /* Filesystem type name */ + const sqlite3_io_methods *pMethods; /* Appropriate locking method */ + } aMap[] = { + { "hfs", &posixIoMethods }, + { "ufs", &posixIoMethods }, + { "afpfs", &afpIoMethods }, + { "smbfs", &afpIoMethods }, + { "webdav", &nolockIoMethods }, + { 0, 0 } + }; + int i; + struct statfs fsInfo; + struct flock lockInfo; -#ifdef SQLITE_DEBUG -/* -** Return a pointer to a nul-terminated string in static memory that -** describes a locking mask. The string is of the form "MSABCD" with -** each character representing a lock. "M" for MUTEX, "S" for DMS, -** and "A" through "D" for the region locks. If a lock is held, the -** letter is shown. If the lock is not held, the letter is converted -** to ".". + if( !filePath ){ + /* If filePath==NULL that means we are dealing with a transient file + ** that does not need to be locked. */ + return &nolockIoMethods; + } + if( statfs(filePath, &fsInfo) != -1 ){ + if( fsInfo.f_flags & MNT_RDONLY ){ + return &nolockIoMethods; + } + for(i=0; aMap[i].zFilesystem; i++){ + if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ + return aMap[i].pMethods; + } + } + } + + /* Default case. Handles, amongst others, "nfs". + ** Test byte-range lock using fcntl(). If the call succeeds, + ** assume that the file-system supports POSIX style locks. + */ + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ + return &nfsIoMethods; + } else { + return &posixIoMethods; + } + }else{ + return &dotlockIoMethods; + } +} +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; + +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ + +#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE +/* +** This "finder" function attempts to determine the best locking strategy +** for the database file "filePath". It then returns the sqlite3_io_methods +** object that implements that strategy. ** -** This routine is for debugging purposes only and does not appear -** in a production build. +** This is for VXWorks only. */ -static const char *unixShmLockString(u8 mask){ - static char zBuf[48]; - static int iBuf = 0; - char *z; +static const sqlite3_io_methods *autolockIoFinderImpl( + const char *filePath, /* name of the database file */ + unixFile *pNew /* the open file object */ +){ + struct flock lockInfo; - z = &zBuf[iBuf]; - iBuf += 8; - if( iBuf>=sizeof(zBuf) ) iBuf = 0; + if( !filePath ){ + /* If filePath==NULL that means we are dealing with a transient file + ** that does not need to be locked. */ + return &nolockIoMethods; + } - z[0] = (mask & UNIX_SHM_DMS) ? 'S' : '.'; - z[1] = (mask & UNIX_SHM_A) ? 'A' : '.'; - z[2] = (mask & UNIX_SHM_B) ? 'B' : '.'; - z[3] = (mask & UNIX_SHM_C) ? 'C' : '.'; - z[4] = (mask & UNIX_SHM_D) ? 'D' : '.'; - z[5] = 0; - return z; + /* Test if fcntl() is supported and use POSIX style locks. + ** Otherwise fall back to the named semaphore method. + */ + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + return &posixIoMethods; + }else{ + return &semIoMethods; + } } -#endif /* SQLITE_DEBUG */ +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; + +#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ /* -** Apply posix advisory locks for all bytes identified in lockMask. -** -** lockMask might contain multiple bits but all bits are guaranteed -** to be contiguous. +** An abstract type for a pointer to a IO method finder function: +*/ +typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); + + +/**************************************************************************** +**************************** sqlite3_vfs methods **************************** ** -** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking -** otherwise. +** This division contains the implementation of methods on the +** sqlite3_vfs object. */ -static int unixShmSystemLock( - unixShmFile *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ - u8 lockMask /* Which bytes to lock or unlock */ + +/* +** Initialize the contents of the unixFile structure pointed to by pId. +*/ +static int fillInUnixFile( + sqlite3_vfs *pVfs, /* Pointer to vfs object */ + int h, /* Open file descriptor of file being opened */ + int dirfd, /* Directory file descriptor */ + sqlite3_file *pId, /* Write to the unixFile structure here */ + const char *zFilename, /* Name of the file being opened */ + int noLock, /* Omit locking if true */ + int isDelete /* Delete on close if true */ ){ - struct flock f; /* The posix advisory locking structure */ - int lockOp; /* The opcode for fcntl() */ - int i; /* Offset into the locking byte range */ - int rc; /* Result code form fcntl() */ - u8 mask; /* Mask of bits in lockMask */ + const sqlite3_io_methods *pLockingStyle; + unixFile *pNew = (unixFile *)pId; + int rc = SQLITE_OK; - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); + assert( pNew->pLock==NULL ); + assert( pNew->pOpen==NULL ); - /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); - f.l_type = lockType; - f.l_whence = SEEK_SET; - if( lockMask==UNIX_SHM_C && lockType!=F_UNLCK ){ - lockOp = F_SETLKW; - OSTRACE(("SHM-LOCK requesting blocking lock\n")); - }else{ - lockOp = F_SETLK; - } + /* Parameter isDelete is only used on vxworks. Express this explicitly + ** here to prevent compiler warnings about unused parameters. + */ + UNUSED_PARAMETER(isDelete); - /* Find the first bit in lockMask that is set */ - for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} - assert( mask!=0 ); - f.l_start = i+UNIX_SHM_BASE; - f.l_len = 1; + OSTRACE3("OPEN %-3d %s\n", h, zFilename); + pNew->h = h; + pNew->dirfd = dirfd; + SET_THREADID(pNew); + pNew->fileFlags = 0; + assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */ + pNew->zPath = zFilename; - /* Extend the locking range for each additional bit that is set */ - mask <<= 1; - while( mask!=0 && (lockMask & mask)!=0 ){ - f.l_len++; - mask <<= 1; +#if OS_VXWORKS + pNew->pId = vxworksFindFileId(zFilename); + if( pNew->pId==0 ){ + noLock = 1; + rc = SQLITE_NOMEM; } +#endif - /* Verify that all bits set in lockMask are contiguous */ - assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + if( noLock ){ + pLockingStyle = &nolockIoMethods; + }else{ + pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); +#if SQLITE_ENABLE_LOCKING_STYLE + /* Cache zFilename in the locking context (AFP and dotlock override) for + ** proxyLock activation is possible (remote proxy is based on db name) + ** zFilename remains valid until file is closed, to support */ + pNew->lockingContext = (void*)zFilename; +#endif + } - /* Acquire the system-level lock */ - rc = fcntl(pFile->h, lockOp, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + if( pLockingStyle == &posixIoMethods +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE + || pLockingStyle == &nfsIoMethods +#endif + ){ + unixEnterMutex(); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( rc!=SQLITE_OK ){ + /* If an error occured in findLockInfo(), close the file descriptor + ** immediately, before releasing the mutex. findLockInfo() may fail + ** in two scenarios: + ** + ** (a) A call to fstat() failed. + ** (b) A malloc failed. + ** + ** Scenario (b) may only occur if the process is holding no other + ** file descriptors open on the same file. If there were other file + ** descriptors on this file, then no malloc would be required by + ** findLockInfo(). If this is the case, it is quite safe to close + ** handle h - as it is guaranteed that no posix locks will be released + ** by doing so. + ** + ** If scenario (a) caused the error then things are not so safe. The + ** implicit assumption here is that if fstat() fails, things are in + ** such bad shape that dropping a lock or two doesn't matter much. + */ + close(h); + h = -1; + } + unixLeaveMutex(); + } - /* Update the global lock state and do debug tracing */ -#ifdef SQLITE_DEBUG - OSTRACE(("SHM-LOCK ")); - if( rc==SQLITE_OK ){ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask &= ~lockMask; - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask |= lockMask; +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) + else if( pLockingStyle == &afpIoMethods ){ + /* AFP locking uses the file path so it needs to be included in + ** the afpLockingContext. + */ + afpLockingContext *pCtx; + pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); + if( pCtx==0 ){ + rc = SQLITE_NOMEM; }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock ok")); - pFile->exclMask |= lockMask; - pFile->sharedMask &= ~lockMask; + /* NB: zFilename exists and remains valid until the file is closed + ** according to requirement F11141. So we do not need to make a + ** copy of the filename. */ + pCtx->dbPath = zFilename; + pCtx->reserved = 0; + srandomdev(); + unixEnterMutex(); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( rc!=SQLITE_OK ){ + sqlite3_free(pNew->lockingContext); + close(h); + h = -1; + } + unixLeaveMutex(); } - }else{ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock failed")); - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); + } +#endif + + else if( pLockingStyle == &dotlockIoMethods ){ + /* Dotfile locking uses the file path so it needs to be included in + ** the dotlockLockingContext + */ + char *zLockFile; + int nFilename; + nFilename = (int)strlen(zFilename) + 6; + zLockFile = (char *)sqlite3_malloc(nFilename); + if( zLockFile==0 ){ + rc = SQLITE_NOMEM; }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock failed")); + sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); } + pNew->lockingContext = zLockFile; + } + +#if OS_VXWORKS + else if( pLockingStyle == &semIoMethods ){ + /* Named semaphore locking uses the file path so it needs to be + ** included in the semLockingContext + */ + unixEnterMutex(); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ + char *zSemName = pNew->pOpen->aSemName; + int n; + sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", + pNew->pId->zCanonicalName); + for( n=1; zSemName[n]; n++ ) + if( zSemName[n]=='/' ) zSemName[n] = '_'; + pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); + if( pNew->pOpen->pSem == SEM_FAILED ){ + rc = SQLITE_NOMEM; + pNew->pOpen->aSemName[0] = '\0'; + } + } + unixLeaveMutex(); } - OSTRACE((" - change requested %s - afterwards %s:%s\n", - unixShmLockString(lockMask), - unixShmLockString(pFile->sharedMask), - unixShmLockString(pFile->exclMask))); #endif + + pNew->lastErrno = 0; +#if OS_VXWORKS + if( rc!=SQLITE_OK ){ + if( h>=0 ) close(h); + h = -1; + unlink(zFilename); + isDelete = 0; + } + pNew->isDelete = isDelete; +#endif + if( rc!=SQLITE_OK ){ + if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ + if( h>=0 ) close(h); + }else{ + pNew->pMethod = pLockingStyle; + OpenCounter(+1); + } + return rc; +} - return rc; +/* +** Open a file descriptor to the directory containing file zFilename. +** If successful, *pFd is set to the opened file descriptor and +** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM +** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined +** value. +** +** If SQLITE_OK is returned, the caller is responsible for closing +** the file descriptor *pFd using close(). +*/ +static int openDirectory(const char *zFilename, int *pFd){ + int ii; + int fd = -1; + char zDirname[MAX_PATHNAME+1]; + + sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); + for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); + if( ii>0 ){ + zDirname[ii] = '\0'; + fd = open(zDirname, O_RDONLY|O_BINARY, 0); + if( fd>=0 ){ +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); +#endif + OSTRACE3("OPENDIR %-3d %s\n", fd, zDirname); + } + } + *pFd = fd; + return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); } /* -** For connection p, unlock all of the locks identified by the unlockMask -** parameter. +** Create a temporary file name in zBuf. zBuf must be allocated +** by the calling process and must be big enough to hold at least +** pVfs->mxPathname bytes. */ -static int unixShmUnlock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to be unlocked */ - u8 unlockMask /* Mask of locks to be unlocked */ -){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ - u8 allMask; /* Union of locks held by connections other than "p" */ +static int getTempname(int nBuf, char *zBuf){ + static const char *azDirs[] = { + 0, + 0, + "/var/tmp", + "/usr/tmp", + "/tmp", + ".", + }; + static const unsigned char zChars[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + unsigned int i, j; + struct stat buf; + const char *zDir = "."; - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. + */ + SimulateIOError( return SQLITE_IOERR ); - /* Compute locks held by sibling connections */ - allMask = 0; - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); - allMask |= pX->sharedMask; + azDirs[0] = sqlite3_temp_directory; + if (NULL == azDirs[1]) { + azDirs[1] = getenv("TMPDIR"); + } + + for(i=0; i= (size_t)nBuf ){ + return SQLITE_ERROR; } - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~unlockMask; - p->sharedMask &= ~unlockMask; - } - return rc; + do{ + sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); + j = (int)strlen(zBuf); + sqlite3_randomness(15, &zBuf[j]); + for(i=0; i<15; i++, j++){ + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + } + zBuf[j] = 0; + }while( access(zBuf,0)==0 ); + return SQLITE_OK; } +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) /* -** Get reader locks for connection p on all locks in the readMask parameter. +** Routine to transform a unixFile into a proxy-locking unixFile. +** Implementation in the proxy-lock division, but used by unixOpen() +** if SQLITE_PREFER_PROXY_LOCKING is defined. */ -static int unixShmSharedLock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to get the shared locks */ - u8 readMask /* Mask of shared locks to be acquired */ -){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ - u8 allShared; /* Union of locks held by connections other than "p" */ +static int proxyTransformUnixFile(unixFile*, const char*); +#endif - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); +/* +** Search for an unused file descriptor that was opened on the database +** file (not a journal or master-journal file) identified by pathname +** zPath with SQLITE_OPEN_XXX flags matching those passed as the second +** argument to this function. +** +** Such a file descriptor may exist if a database connection was closed +** but the associated file descriptor could not be closed because some +** other file descriptor open on the same file is holding a file-lock. +** Refer to comments in the unixClose() function and the lengthy comment +** describing "Posix Advisory Locking" at the start of this file for +** further details. Also, ticket #4018. +** +** If a suitable file descriptor is found, then it is returned. If no +** such file descriptor is located, -1 is returned. +*/ +static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ + UnixUnusedFd *pUnused = 0; - /* Find out which shared locks are already held by sibling connections. - ** If any sibling already holds an exclusive lock, go ahead and return - ** SQLITE_BUSY. - */ - allShared = 0; - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; - allShared |= pX->sharedMask; - } + /* Do not search for an unused file descriptor on vxworks. Not because + ** vxworks would not benefit from the change (it might, we're not sure), + ** but because no way to test it is currently available. It is better + ** not to risk breaking vxworks support for the sake of such an obscure + ** feature. */ +#if !OS_VXWORKS + struct stat sStat; /* Results of stat() call */ - /* Get shared locks at the system level, if necessary */ - if( (~allShared) & readMask ){ - rc = unixShmSystemLock(pFile, F_RDLCK, readMask); - }else{ - rc = SQLITE_OK; - } + /* A stat() call may fail for various reasons. If this happens, it is + ** almost certain that an open() call on the same path will also fail. + ** For this reason, if an error occurs in the stat() call here, it is + ** ignored and -1 is returned. The caller will try to open a new file + ** descriptor on the same path, fail, and return an error to SQLite. + ** + ** Even if a subsequent open() call does succeed, the consequences of + ** not searching for a resusable file descriptor are not dire. */ + if( 0==stat(zPath, &sStat) ){ + struct unixOpenCnt *pOpen; - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= readMask; + unixEnterMutex(); + pOpen = openList; + while( pOpen && (pOpen->fileId.dev!=sStat.st_dev + || pOpen->fileId.ino!=sStat.st_ino) ){ + pOpen = pOpen->pNext; + } + if( pOpen ){ + UnixUnusedFd **pp; + for(pp=&pOpen->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); + pUnused = *pp; + if( pUnused ){ + *pp = pUnused->pNext; + } + } + unixLeaveMutex(); } - return rc; +#endif /* if !OS_VXWORKS */ + return pUnused; } /* -** For connection p, get an exclusive lock on all locks identified in -** the writeMask parameter. +** Open the file zPath. +** +** Previously, the SQLite OS layer used three functions in place of this +** one: +** +** sqlite3OsOpenReadWrite(); +** sqlite3OsOpenReadOnly(); +** sqlite3OsOpenExclusive(); +** +** These calls correspond to the following combinations of flags: +** +** ReadWrite() -> (READWRITE | CREATE) +** ReadOnly() -> (READONLY) +** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) +** +** The old OpenExclusive() accepted a boolean argument - "delFlag". If +** true, the file was configured to be automatically deleted when the +** file handle closed. To achieve the same effect using this new +** interface, add the DELETEONCLOSE flag to those specified above for +** OpenExclusive(). */ -static int unixShmExclusiveLock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to get the exclusive locks */ - u8 writeMask /* Mask of exclusive locks to be acquired */ +static int unixOpen( + sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ + const char *zPath, /* Pathname of file to be opened */ + sqlite3_file *pFile, /* The file descriptor to be filled in */ + int flags, /* Input flags to control the opening */ + int *pOutFlags /* Output flags returned to SQLite core */ ){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ + unixFile *p = (unixFile *)pFile; + int fd = -1; /* File descriptor returned by open() */ + int dirfd = -1; /* Directory file descriptor */ + int openFlags = 0; /* Flags to pass to open() */ + int eType = flags&0xFFFFFF00; /* Type of file to open */ + int noLock; /* True to omit locking primitives */ + int rc = SQLITE_OK; /* Function Return Code */ - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#if SQLITE_ENABLE_LOCKING_STYLE + int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); +#endif - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. + /* If creating a master or main-file journal, this function will open + ** a file-descriptor on the directory too. The first time unixSync() + ** is called the directory file descriptor will be fsync()ed and close()d. */ - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; - if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; - } + int isOpenDirectory = (isCreate && + (eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL) + ); - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. + /* If argument zPath is a NULL pointer, this function is required to open + ** a temporary file. Use this buffer to store the file name in. */ - rc = unixShmSystemLock(pFile, F_WRLCK, writeMask); - if( rc==SQLITE_OK ){ - p->sharedMask &= ~writeMask; - p->exclMask |= writeMask; + char zTmpname[MAX_PATHNAME+1]; + const char *zName = zPath; + + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); + + /* The main DB, main journal, and master journal are never automatically + ** deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB + ); + + memset(p, 0, sizeof(unixFile)); + + if( eType==SQLITE_OPEN_MAIN_DB ){ + UnixUnusedFd *pUnused; + pUnused = findReusableFd(zName, flags); + if( pUnused ){ + fd = pUnused->fd; + }else{ + pUnused = sqlite3_malloc(sizeof(*pUnused)); + if( !pUnused ){ + return SQLITE_NOMEM; + } + } + p->pUnused = pUnused; + }else if( !zName ){ + /* If zName is NULL, the upper layer is requesting a temp file. */ + assert(isDelete && !isOpenDirectory); + rc = getTempname(MAX_PATHNAME+1, zTmpname); + if( rc!=SQLITE_OK ){ + return rc; + } + zName = zTmpname; } - return rc; -} -/* -** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0. -** -** This is not a VFS shared-memory method; it is a utility function called -** by VFS shared-memory methods. -*/ -static void unixShmPurge(void){ - unixShmFile **pp; - unixShmFile *p; - assert( unixMutexHeld() ); - pp = &unixShmFileList; - while( (p = *pp)!=0 ){ - if( p->nRef==0 ){ - if( p->mutex ) sqlite3_mutex_free(p->mutex); - if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); - if( p->h>=0 ) close(p->h); - *pp = p->pNext; - sqlite3_free(p); - }else{ - pp = &p->pNext; + /* Determine the value of the flags parameter passed to POSIX function + ** open(). These must be calculated even if open() is not called, as + ** they may be stored as part of the file handle and used by the + ** 'conch file' locking functions later on. */ + if( isReadonly ) openFlags |= O_RDONLY; + if( isReadWrite ) openFlags |= O_RDWR; + if( isCreate ) openFlags |= O_CREAT; + if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); + openFlags |= (O_LARGEFILE|O_BINARY); + + if( fd<0 ){ + mode_t openMode = (isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = open(zName, openFlags, openMode); + OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); + if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ + /* Failed to open the file for read/write access. Try read-only. */ + flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + openFlags &= ~(O_RDWR|O_CREAT); + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + fd = open(zName, openFlags, openMode); + } + if( fd<0 ){ + rc = SQLITE_CANTOPEN_BKPT; + goto open_finished; } } -} + assert( fd>=0 ); + if( pOutFlags ){ + *pOutFlags = flags; + } -/* -** Open a shared-memory area. This particular implementation uses -** mmapped files. -** -** zName is a filename used to identify the shared-memory area. The -** implementation does not (and perhaps should not) use this name -** directly, but rather use it as a template for finding an appropriate -** name for the shared-memory storage. In this implementation, the -** string "-index" is appended to zName and used as the name of the -** mmapped file. -** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. -*/ -static int unixShmOpen( - sqlite3_vfs *pVfs, /* The VFS */ - const char *zName, /* Name of the corresponding database file */ - sqlite3_shm **pShm /* Write the unixShm object created here */ -){ - struct unixShm *p = 0; /* The connection to be opened */ - struct unixShmFile *pFile = 0; /* The underlying mmapped file */ - int rc; /* Result code */ - struct unixFileId fid; /* Unix file identifier */ - struct unixShmFile *pNew; /* Newly allocated pFile */ - struct stat sStat; /* Result from stat() an fstat() */ - int nName; /* Size of zName in bytes */ + if( p->pUnused ){ + p->pUnused->fd = fd; + p->pUnused->flags = flags; + } - /* Allocate space for the new sqlite3_shm object. Also speculatively - ** allocate space for a new unixShmFile and filename. - */ - p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM; - memset(p, 0, sizeof(*p)); - nName = strlen(zName); - pNew = sqlite3_malloc( sizeof(*pFile) + nName + 15 ); - if( pNew==0 ){ - sqlite3_free(p); - return SQLITE_NOMEM; + if( isDelete ){ +#if OS_VXWORKS + zPath = zName; +#else + unlink(zName); +#endif } - memset(pNew, 0, sizeof(*pNew)); - pNew->zFilename = (char*)&pNew[1]; - sqlite3_snprintf(nName+12, pNew->zFilename, "%s-wal-index", zName); +#if SQLITE_ENABLE_LOCKING_STYLE + else{ + p->openFlags = openFlags; + } +#endif - /* Look to see if there is an existing unixShmFile that can be used. - ** If no matching unixShmFile currently exists, create a new one. - */ - unixEnterMutex(); - rc = stat(pNew->zFilename, &sStat); - if( rc==0 ){ - memset(&fid, 0, sizeof(fid)); - fid.dev = sStat.st_dev; - fid.ino = sStat.st_ino; - for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){ - if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break; + if( isOpenDirectory ){ + rc = openDirectory(zPath, &dirfd); + if( rc!=SQLITE_OK ){ + /* It is safe to close fd at this point, because it is guaranteed not + ** to be open on a database file. If it were open on a database file, + ** it would not be safe to close as this would release any locks held + ** on the file by this process. */ + assert( eType!=SQLITE_OPEN_MAIN_DB ); + close(fd); /* silently leak if fail, already in error */ + goto open_finished; } } - if( pFile ){ - sqlite3_free(pNew); - }else{ - pFile = pNew; - pNew = 0; - pFile->h = -1; - pFile->pNext = unixShmFileList; - unixShmFileList = pFile; - pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pFile->mutex==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } - pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pFile->mutexBuf==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); +#endif - pFile->h = open(pFile->zFilename, O_RDWR|O_CREAT, 0664); - if( pFile->h<0 ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } + noLock = eType!=SQLITE_OPEN_MAIN_DB; - rc = fstat(pFile->h, &sStat); - if( rc ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } - pFile->fid.dev = sStat.st_dev; - pFile->fid.ino = sStat.st_ino; + +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE + struct statfs fsInfo; + if( fstatfs(fd, &fsInfo) == -1 ){ + ((unixFile*)pFile)->lastErrno = errno; + if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ + close(fd); /* silently leak if fail, in error */ + return SQLITE_IOERR_ACCESS; + } + if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + } +#endif + +#if SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_PREFER_PROXY_LOCKING + isAutoProxy = 1; +#endif + if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ + char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); + int useProxy = 0; - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){ - if( ftruncate(pFile->h, 0) ){ - rc = SQLITE_IOERR; + /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means + ** never use proxy, NULL means use proxy for non-local files only. */ + if( envforce!=NULL ){ + useProxy = atoi(envforce)>0; + }else{ + struct statfs fsInfo; + if( statfs(zPath, &fsInfo) == -1 ){ + /* In theory, the close(fd) call is sub-optimal. If the file opened + ** with fd is a database file, and there are other connections open + ** on that file that are currently holding advisory locks on it, + ** then the call to close() will cancel those locks. In practice, + ** we're assuming that statfs() doesn't fail very often. At least + ** not while other file descriptors opened by the same process on + ** the same file are working. */ + p->lastErrno = errno; + if( dirfd>=0 ){ + close(dirfd); /* silently leak if fail, in error */ + } + close(fd); /* silently leak if fail, in error */ + rc = SQLITE_IOERR_ACCESS; + goto open_finished; } + useProxy = !(fsInfo.f_flags&MNT_LOCAL); } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pFile, F_RDLCK, UNIX_SHM_DMS); + if( useProxy ){ + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + if( rc==SQLITE_OK ){ + rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); + if( rc!=SQLITE_OK ){ + /* Use unixClose to clean up the resources added in fillInUnixFile + ** and clear all the structure's references. Specifically, + ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op + */ + unixClose(pFile); + return rc; + } + } + goto open_finished; } - if( rc ) goto shm_open_err; } +#endif + + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); +open_finished: + if( rc!=SQLITE_OK ){ + sqlite3_free(p->pUnused); + } + return rc; +} - /* Make the new connection a child of the unixShmFile */ - p->pFile = pFile; - p->pNext = pFile->pFirst; -#ifdef SQLITE_DEBUG - p->id = pFile->nextShmId++; + +/* +** Delete the file at zPath. If the dirSync argument is true, fsync() +** the directory after deleting the file. +*/ +static int unixDelete( + sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ + const char *zPath, /* Name of file to be deleted */ + int dirSync /* If true, fsync() directory after deleting file */ +){ + int rc = SQLITE_OK; + UNUSED_PARAMETER(NotUsed); + SimulateIOError(return SQLITE_IOERR_DELETE); + unlink(zPath); +#ifndef SQLITE_DISABLE_DIRSYNC + if( dirSync ){ + int fd; + rc = openDirectory(zPath, &fd); + if( rc==SQLITE_OK ){ +#if OS_VXWORKS + if( fsync(fd)==-1 ) +#else + if( fsync(fd) ) +#endif + { + rc = SQLITE_IOERR_DIR_FSYNC; + } + if( close(fd)&&!rc ){ + rc = SQLITE_IOERR_DIR_CLOSE; + } + } + } #endif - pFile->pFirst = p; - pFile->nRef++; - *pShm = (sqlite3_shm*)p; - unixLeaveMutex(); - return SQLITE_OK; - - /* Jump here on any error */ -shm_open_err: - unixShmPurge(); /* This call frees pFile if required */ - sqlite3_free(p); - sqlite3_free(pNew); - *pShm = 0; - unixLeaveMutex(); return rc; } /* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. +** Test the existance of or access permissions of file zPath. The +** test performed depends on the value of flags: +** +** SQLITE_ACCESS_EXISTS: Return 1 if the file exists +** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. +** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. +** +** Otherwise return 0. */ -static int unixShmClose( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* The shared-memory to be closed */ - int deleteFlag /* Delete after closing if true */ +static int unixAccess( + sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ + const char *zPath, /* Path of the file to examine */ + int flags, /* What do we want to learn about the zPath file? */ + int *pResOut /* Write result boolean here */ ){ - unixShm *p; /* The connection to be closed */ - unixShmFile *pFile; /* The underlying shared-memory file */ - unixShm **pp; /* For looping over sibling connections */ - - UNUSED_PARAMETER(pVfs); - if( pSharedMem==0 ) return SQLITE_OK; - p = (struct unixShm*)pSharedMem; - pFile = p->pFile; - - /* Verify that the connection being closed holds no locks */ - assert( p->exclMask==0 ); - assert( p->sharedMask==0 ); - - /* Remove connection p from the set of connections associated with pFile */ - sqlite3_mutex_enter(pFile->mutex); - for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; - - /* Free the connection p */ - sqlite3_free(p); - sqlite3_mutex_leave(pFile->mutex); + int amode = 0; + UNUSED_PARAMETER(NotUsed); + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + switch( flags ){ + case SQLITE_ACCESS_EXISTS: + amode = F_OK; + break; + case SQLITE_ACCESS_READWRITE: + amode = W_OK|R_OK; + break; + case SQLITE_ACCESS_READ: + amode = R_OK; + break; - /* If pFile->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - unixEnterMutex(); - assert( pFile->nRef>0 ); - pFile->nRef--; - if( pFile->nRef==0 ){ - if( deleteFlag ) unlink(pFile->zFilename); - unixShmPurge(); + default: + assert(!"Invalid flags argument"); } - unixLeaveMutex(); - + *pResOut = (access(zPath, amode)==0); return SQLITE_OK; } + /* -** Query and/or changes the size of the underlying storage for -** a shared-memory segment. The reqSize parameter is the new size -** of the underlying storage, or -1 to do just a query. The size -** of the underlying storage (after resizing if resizing occurs) is -** written into pNewSize. -** -** This routine does not (necessarily) change the size of the mapping -** of the underlying storage into memory. Use xShmGet() to change -** the mapping size. +** Turn a relative pathname into a full pathname. The relative path +** is stored as a nul-terminated string in the buffer pointed to by +** zPath. ** -** The reqSize parameter is the minimum size requested. The implementation -** is free to expand the storage to some larger amount if it chooses. +** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes +** (in this case, MAX_PATHNAME bytes). The full-path is written to +** this buffer before returning. */ -static int unixShmSize( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */ - int reqSize, /* Requested size. -1 for query only */ - int *pNewSize /* Write new size here */ +static int unixFullPathname( + sqlite3_vfs *pVfs, /* Pointer to vfs object */ + const char *zPath, /* Possibly relative input path */ + int nOut, /* Size of output buffer in bytes */ + char *zOut /* Output buffer */ ){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_OK; - struct stat sStat; + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. This function could fail if, for example, the + ** current working directory has been unlinked. + */ + SimulateIOError( return SQLITE_ERROR ); + + assert( pVfs->mxPathname==MAX_PATHNAME ); UNUSED_PARAMETER(pVfs); - if( reqSize>=0 ){ - reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; - reqSize *= SQLITE_UNIX_SHM_INCR; - rc = ftruncate(pFile->h, reqSize); - } - if( fstat(pFile->h, &sStat)==0 ){ - *pNewSize = (int)sStat.st_size; + zOut[nOut-1] = '\0'; + if( zPath[0]=='/' ){ + sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ - *pNewSize = 0; - rc = SQLITE_IOERR; + int nCwd; + if( getcwd(zOut, nOut-1)==0 ){ + return SQLITE_CANTOPEN_BKPT; + } + nCwd = (int)strlen(zOut); + sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); } - return rc; + return SQLITE_OK; } +#ifndef SQLITE_OMIT_LOAD_EXTENSION /* -** Map the shared storage into memory. The minimum size of the -** mapping should be reqMapSize if reqMapSize is positive. If -** reqMapSize is zero or negative, the implementation can choose -** whatever mapping size is convenient. -** -** *ppBuf is made to point to the memory which is a mapping of the -** underlying storage. A mutex is acquired to prevent other threads -** from running while *ppBuf is in use in order to prevent other threads -** remapping *ppBuf out from under this thread. The unixShmRelease() -** call will release the mutex. However, if the lock state is CHECKPOINT, -** the mutex is not acquired because CHECKPOINT will never remap the -** buffer. RECOVER might remap, though, so CHECKPOINT will acquire -** the mutex if and when it promotes to RECOVER. -** -** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from -** being remapped also prevents more than one thread from being in -** RECOVER at a time. But, RECOVER sometimes wants to remap itself. -** To prevent RECOVER from losing its lock while remapping, the -** mutex is not released by unixShmRelease() when in RECOVER. -** -** *pNewMapSize is set to the size of the mapping. -** -** *ppBuf and *pNewMapSize might be NULL and zero if no space has -** yet been allocated to the underlying storage. +** Interfaces for opening a shared library, finding entry points +** within the shared library, and closing the shared library. */ -static int unixShmGet( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */ - int reqMapSize, /* Requested size of mapping. -1 means don't care */ - int *pNewMapSize, /* Write new size of mapping here */ - void **ppBuf /* Write mapping buffer origin here */ -){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_OK; +#include +static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){ + UNUSED_PARAMETER(NotUsed); + return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); +} - if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){ - assert( sqlite3_mutex_notheld(pFile->mutex) ); - sqlite3_mutex_enter(pFile->mutexBuf); - p->hasMutexBuf = 1; +/* +** SQLite calls this function immediately after a call to unixDlSym() or +** unixDlOpen() fails (returns a null pointer). If a more detailed error +** message is available, it is written to zBufOut. If no error message +** is available, zBufOut is left unmodified and SQLite uses a default +** error message. +*/ +static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ + char *zErr; + UNUSED_PARAMETER(NotUsed); + unixEnterMutex(); + zErr = dlerror(); + if( zErr ){ + sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); } - sqlite3_mutex_enter(pFile->mutex); - if( pFile->szMap==0 || reqMapSize>pFile->szMap ){ - int actualSize; - if( unixShmSize(pVfs, pSharedMem, -1, &actualSize)==SQLITE_OK - && reqMapSizepMMapBuf ){ - munmap(pFile->pMMapBuf, pFile->szMap); + unixLeaveMutex(); +} +static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ + /* + ** GCC with -pedantic-errors says that C90 does not allow a void* to be + ** cast into a pointer to a function. And yet the library dlsym() routine + ** returns a void* which is really a pointer to a function. So how do we + ** use dlsym() with -pedantic-errors? + ** + ** Variable x below is defined to be a pointer to a function taking + ** parameters void* and const char* and returning a pointer to a function. + ** We initialize x by assigning it a pointer to the dlsym() function. + ** (That assignment requires a cast.) Then we call the function that + ** x points to. + ** + ** This work-around is unlikely to work correctly on any system where + ** you really cannot cast a function pointer into void*. But then, on the + ** other hand, dlsym() will not work on such a system either, so we have + ** not really lost anything. + */ + void (*(*x)(void*,const char*))(void); + UNUSED_PARAMETER(NotUsed); + x = (void(*(*)(void*,const char*))(void))dlsym; + return (*x)(p, zSym); +} +static void unixDlClose(sqlite3_vfs *NotUsed, void *pHandle){ + UNUSED_PARAMETER(NotUsed); + dlclose(pHandle); +} +#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ + #define unixDlOpen 0 + #define unixDlError 0 + #define unixDlSym 0 + #define unixDlClose 0 +#endif + +/* +** Write nBuf bytes of random data to the supplied buffer zBuf. +*/ +static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ + UNUSED_PARAMETER(NotUsed); + assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); + + /* We have to initialize zBuf to prevent valgrind from reporting + ** errors. The reports issued by valgrind are incorrect - we would + ** prefer that the randomness be increased by making use of the + ** uninitialized space in zBuf - but valgrind errors tend to worry + ** some users. Rather than argue, it seems easier just to initialize + ** the whole array and silence valgrind, even if that means less randomness + ** in the random seed. + ** + ** When testing, initializing zBuf[] to zero is all we do. That means + ** that we always use the same random number sequence. This makes the + ** tests repeatable. + */ + memset(zBuf, 0, nBuf); +#if !defined(SQLITE_TEST) + { + int pid, fd; + fd = open("/dev/urandom", O_RDONLY); + if( fd<0 ){ + time_t t; + time(&t); + memcpy(zBuf, &t, sizeof(t)); + pid = getpid(); + memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); + assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); + nBuf = sizeof(t) + sizeof(pid); + }else{ + nBuf = read(fd, zBuf, nBuf); + close(fd); } - pFile->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, - pFile->h, 0); - pFile->szMap = pFile->pMMapBuf ? reqMapSize : 0; } - *pNewMapSize = pFile->szMap; - *ppBuf = pFile->pMMapBuf; - sqlite3_mutex_leave(pFile->mutex); - return rc; +#endif + return nBuf; } + /* -** Release the lock held on the shared memory segment to that other -** threads are free to resize it if necessary. -** -** If the lock is not currently held, this routine is a harmless no-op. -** -** If the shared-memory object is in lock state RECOVER, then we do not -** really want to release the lock, so in that case too, this routine -** is a no-op. +** Sleep for a little while. Return the amount of time slept. +** The argument is the number of microseconds we want to sleep. +** The return value is the number of microseconds of sleep actually +** requested from the underlying operating system, a number which +** might be greater than or equal to the argument, but not less +** than the argument. */ -static int unixShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *pSharedMem){ - unixShm *p = (unixShm*)pSharedMem; - UNUSED_PARAMETER(pVfs); - if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){ - assert( sqlite3_mutex_notheld(p->pFile->mutex) ); - sqlite3_mutex_leave(p->pFile->mutexBuf); - p->hasMutexBuf = 0; - } - return SQLITE_OK; +static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ +#if OS_VXWORKS + struct timespec sp; + + sp.tv_sec = microseconds / 1000000; + sp.tv_nsec = (microseconds % 1000000) * 1000; + nanosleep(&sp, NULL); + UNUSED_PARAMETER(NotUsed); + return microseconds; +#elif defined(HAVE_USLEEP) && HAVE_USLEEP + usleep(microseconds); + UNUSED_PARAMETER(NotUsed); + return microseconds; +#else + int seconds = (microseconds+999999)/1000000; + sleep(seconds); + UNUSED_PARAMETER(NotUsed); + return seconds*1000000; +#endif } /* -** Symbolic names for LOCK states used for debugging. +** The following variable, if set to a non-zero value, is interpreted as +** the number of seconds since 1970 and is used to set the result of +** sqlite3OsCurrentTime() during testing. */ -#ifdef SQLITE_DEBUG -static const char *azLkName[] = { - "UNLOCK", - "READ", - "READ_FULL", - "WRITE", - "PENDING", - "CHECKPOINT", - "RECOVER" -}; +#ifdef SQLITE_TEST +int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif - /* -** Change the lock state for a shared-memory segment. +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return 0. Return 1 if the time and date cannot be found. */ -static int unixShmLock( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer from unixShmOpen() */ - int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ - int *pGotLock /* The lock you actually got */ -){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_PROTOCOL; - - UNUSED_PARAMETER(pVfs); - - /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never - ** directly requested; they are side effects from requesting - ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. - */ - assert( desiredLock==SQLITE_SHM_UNLOCK - || desiredLock==SQLITE_SHM_READ - || desiredLock==SQLITE_SHM_WRITE - || desiredLock==SQLITE_SHM_CHECKPOINT - || desiredLock==SQLITE_SHM_RECOVER ); +static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; +#if defined(NO_GETTOD) + time_t t; + time(&t); + *piNow = ((sqlite3_int64)i)*1000 + unixEpoch; +#elif OS_VXWORKS + struct timespec sNow; + clock_gettime(CLOCK_REALTIME, &sNow); + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; +#else + struct timeval sNow; + gettimeofday(&sNow, 0); + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; +#endif - /* Return directly if this is just a lock state query, or if - ** the connection is already in the desired locking state. - */ - if( desiredLock==p->lockState - || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) - ){ - OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n", - p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; - return SQLITE_OK; +#ifdef SQLITE_TEST + if( sqlite3_current_time ){ + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } +#endif + UNUSED_PARAMETER(NotUsed); + return 0; +} - OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n", - p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock])); - - if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ - assert( sqlite3_mutex_notheld(pFile->mutex) ); - sqlite3_mutex_enter(pFile->mutexBuf); - p->hasMutexBuf = 1; - } - sqlite3_mutex_enter(pFile->mutex); - switch( desiredLock ){ - case SQLITE_SHM_UNLOCK: { - assert( p->lockState!=SQLITE_SHM_RECOVER ); - unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D); - rc = SQLITE_OK; - p->lockState = SQLITE_SHM_UNLOCK; - break; - } - case SQLITE_SHM_READ: { - if( p->lockState==SQLITE_SHM_UNLOCK ){ - int nAttempt; - rc = SQLITE_BUSY; - assert( p->lockState==SQLITE_SHM_UNLOCK ); - for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ - rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B); - if( rc==SQLITE_BUSY ){ - rc = unixShmSharedLock(pFile, p, UNIX_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_READ_FULL; - } - }else{ - unixShmUnlock(pFile, p, UNIX_SHM_B); - p->lockState = SQLITE_SHM_READ; - } - } - }else{ - assert( p->lockState==SQLITE_SHM_WRITE - || p->lockState==SQLITE_SHM_RECOVER ); - rc = unixShmSharedLock(pFile, p, UNIX_SHM_A); - unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); - p->lockState = SQLITE_SHM_READ; - } - break; - } - case SQLITE_SHM_WRITE: { - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL ); - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_WRITE; - } - break; - } - case SQLITE_SHM_CHECKPOINT: { - assert( p->lockState==SQLITE_SHM_UNLOCK - || p->lockState==SQLITE_SHM_PENDING - ); - if( p->lockState==SQLITE_SHM_UNLOCK ){ - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_PENDING; - } - } - if( p->lockState==SQLITE_SHM_PENDING ){ - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_CHECKPOINT; - } - } - break; - } - default: { - assert( desiredLock==SQLITE_SHM_RECOVER ); - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL - ); - assert( sqlite3_mutex_held(pFile->mutexBuf) ); - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_RECOVER; - } - break; - } - } - sqlite3_mutex_leave(pFile->mutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n", - p->id, getpid(), azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; - return rc; +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ + sqlite3_int64 i; + unixCurrentTimeInt64(0, &i); + *prNow = i/86400000.0; + return 0; +} + +/* +** We added the xGetLastError() method with the intention of providing +** better low-level error messages when operating-system problems come up +** during SQLite operation. But so far, none of that has been implemented +** in the core. So this routine is never called. For now, it is merely +** a place-holder. +*/ +static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ + UNUSED_PARAMETER(NotUsed); + UNUSED_PARAMETER(NotUsed2); + UNUSED_PARAMETER(NotUsed3); + return 0; } -#else -# define unixShmOpen 0 -# define unixShmSize 0 -# define unixShmGet 0 -# define unixShmRelease 0 -# define unixShmLock 0 -# define unixShmClose 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ /* ************************ End of sqlite3_vfs methods *************************** @@ -6600,12 +6617,6 @@ int sqlite3_os_init(void){ unixSleep, /* xSleep */ \ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ - unixShmOpen, /* xShmOpen */ \ - unixShmSize, /* xShmSize */ \ - unixShmGet, /* xShmGet */ \ - unixShmRelease, /* xShmRelease */ \ - unixShmLock, /* xShmLock */ \ - unixShmClose, /* xShmClose */ \ 0, /* xRename */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ } diff --git a/src/pager.c b/src/pager.c index 672789c94..42403c997 100644 --- a/src/pager.c +++ b/src/pager.c @@ -3077,7 +3077,7 @@ int sqlite3PagerClose(Pager *pPager){ pPager->errCode = 0; pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL - sqlite3WalClose(pPager->pWal, pPager->fd, + sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); @@ -5878,7 +5878,7 @@ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; - rc = sqlite3WalCheckpoint(pPager->pWal, pPager->fd, + rc = sqlite3WalCheckpoint(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, zBuf, pPager->xBusyHandler, pPager->pBusyHandlerArg @@ -5891,6 +5891,15 @@ int sqlite3PagerWalCallback(Pager *pPager){ return sqlite3WalCallback(pPager->pWal); } +/* +** Return true if the underlying VFS for the given pager supports the +** primitives necessary for write-ahead logging. +*/ +int sqlite3PagerWalSupported(Pager *pPager){ + const sqlite3_io_methods *pMethods = pPager->fd->pMethods; + return pMethods->iVersion>=2 && pMethods->xShmOpen!=0; +} + /* ** Open a connection to the write-ahead log file for pager pPager. If ** the log connection is already open, this function is a no-op. @@ -5903,12 +5912,14 @@ int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen){ assert( pPager->state>=PAGER_SHARED ); if( !pPager->pWal ){ + if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ - rc = sqlite3WalOpen(pPager->pVfs, pPager->zFilename, &pPager->pWal); + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, + pPager->zFilename, &pPager->pWal); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; } @@ -5944,7 +5955,8 @@ int sqlite3PagerCloseWal(Pager *pPager){ rc = pagerHasWAL(pPager, &logexists); } if( rc==SQLITE_OK && logexists ){ - rc = sqlite3WalOpen(pPager->pVfs, pPager->zFilename, &pPager->pWal); + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, + pPager->zFilename, &pPager->pWal); } } @@ -5954,8 +5966,8 @@ int sqlite3PagerCloseWal(Pager *pPager){ if( rc==SQLITE_OK && pPager->pWal ){ rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ - rc = sqlite3WalClose(pPager->pWal, pPager->fd, - (pPager->noSync ? 0 : pPager->sync_flags), + rc = sqlite3WalClose(pPager->pWal, + (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; diff --git a/src/pager.h b/src/pager.h index 91b2cb32f..57ceb0463 100644 --- a/src/pager.h +++ b/src/pager.h @@ -135,6 +135,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); +int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 15daf971f..af36382ed 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -653,6 +653,14 @@ struct sqlite3_io_methods { int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); + /* Methods above are valid for version 1 */ + int (*xShmOpen)(sqlite3_file*); + int (*xShmSize)(sqlite3_file*, int reqSize, int *pNewSize); + int (*xShmGet)(sqlite3_file*, int reqSize, int *pSize, void**); + int (*xShmRelease)(sqlite3_file*); + int (*xShmLock)(sqlite3_file*, int desiredLock, int *gotLock); + int (*xShmClose)(sqlite3_file*, int deleteFlag); + /* Methods above are valid for version 2 */ /* Additional methods may be added in future releases */ }; @@ -818,7 +826,6 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** */ typedef struct sqlite3_vfs sqlite3_vfs; -typedef struct sqlite3_shm sqlite3_shm; struct sqlite3_vfs { int iVersion; /* Structure version number (currently 2) */ int szOsFile; /* Size of subclassed sqlite3_file */ @@ -843,12 +850,6 @@ struct sqlite3_vfs { ** The methods above are in version 1 of the sqlite_vfs object ** definition. Those that follow are added in version 2 or later */ - int (*xShmOpen)(sqlite3_vfs*, const char *zName, sqlite3_shm**); - int (*xShmSize)(sqlite3_vfs*, sqlite3_shm*, int reqSize, int *pNewSize); - int (*xShmGet)(sqlite3_vfs*, sqlite3_shm*, int reqSize, int *pSize, void**); - int (*xShmRelease)(sqlite3_vfs*, sqlite3_shm*); - int (*xShmLock)(sqlite3_vfs*, sqlite3_shm*, int desiredLock, int *gotLock); - int (*xShmClose)(sqlite3_vfs*, sqlite3_shm*, int deleteFlag); int (*xRename)(sqlite3_vfs*, const char *zOld, const char *zNew, int dirSync); int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* diff --git a/src/test6.c b/src/test6.c index 1ebde80e1..9b5c3f615 100644 --- a/src/test6.c +++ b/src/test6.c @@ -520,8 +520,31 @@ static int cfDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceCharacteristics; } +/* +** Pass-throughs for WAL support. +*/ +static int cfShmOpen(sqlite3_file *pFile){ + return sqlite3OsShmOpen(((CrashFile*)pFile)->pRealFile); +} +static int cfShmSize(sqlite3_file *pFile, int reqSize, int *pNew){ + return sqlite3OsShmSize(((CrashFile*)pFile)->pRealFile, reqSize, pNew); +} +static int cfShmGet(sqlite3_file *pFile, int reqSize, int *pSize, void **pp){ + return sqlite3OsShmGet(((CrashFile*)pFile)->pRealFile, reqSize, pSize, pp); +} +static int cfShmRelease(sqlite3_file *pFile){ + return sqlite3OsShmRelease(((CrashFile*)pFile)->pRealFile); +} +static int cfShmLock(sqlite3_file *pFile, int desired, int *pGot){ + return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, desired, pGot); +} +static int cfShmClose(sqlite3_file *pFile, int delFlag){ + return sqlite3OsShmClose(((CrashFile*)pFile)->pRealFile, delFlag); +} + + static const sqlite3_io_methods CrashFileVtab = { - 1, /* iVersion */ + 2, /* iVersion */ cfClose, /* xClose */ cfRead, /* xRead */ cfWrite, /* xWrite */ @@ -533,7 +556,13 @@ static const sqlite3_io_methods CrashFileVtab = { cfCheckReservedLock, /* xCheckReservedLock */ cfFileControl, /* xFileControl */ cfSectorSize, /* xSectorSize */ - cfDeviceCharacteristics /* xDeviceCharacteristics */ + cfDeviceCharacteristics, /* xDeviceCharacteristics */ + cfShmOpen, /* xShmOpen */ + cfShmSize, /* xShmSize */ + cfShmGet, /* xShmGet */ + cfShmRelease, /* xShmRelease */ + cfShmLock, /* xShmLock */ + cfShmClose /* xShmClose */ }; /* @@ -657,33 +686,6 @@ static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; return pVfs->xCurrentTime(pVfs, pTimeOut); } -static int cfShmOpen(sqlite3_vfs *pCfVfs, const char *zName, sqlite3_shm **pp){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmOpen(pVfs, zName, pp); -} -static int cfShmSize(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int reqSize, int *pNew){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmSize(pVfs, p, reqSize, pNew); -} -static int cfShmGet(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int reqSize, int *pSize, void **pp){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmGet(pVfs, p, reqSize, pSize, pp); -} -static int cfShmRelease(sqlite3_vfs *pCfVfs, sqlite3_shm *p){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmRelease(pVfs, p); -} -static int cfShmLock(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int desiredLock, int *gotLock){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmLock(pVfs, p, desiredLock, gotLock); -} -static int cfShmClose(sqlite3_vfs *pCfVfs, sqlite3_shm *p, int delFlag){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmClose(pVfs, p, delFlag); -} static int processDevSymArgs( Tcl_Interp *interp, @@ -810,12 +812,6 @@ static int crashEnableCmd( cfSleep, /* xSleep */ cfCurrentTime, /* xCurrentTime */ 0, /* xGetlastError */ - cfShmOpen, /* xShmOpen */ - cfShmSize, /* xShmSize */ - cfShmGet, /* xShmGet */ - cfShmRelease, /* xShmRelease */ - cfShmLock, /* xShmLock */ - cfShmClose, /* xShmClose */ 0, /* xRename */ 0, /* xCurrentTimeInt64 */ }; @@ -838,11 +834,6 @@ static int crashEnableCmd( crashVfs.mxPathname = pOriginalVfs->mxPathname; crashVfs.pAppData = (void *)pOriginalVfs; crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile; - if( pOriginalVfs->iVersion<2 || pOriginalVfs->xShmOpen==0 ){ - crashVfs.xShmOpen = 0; - }else{ - crashVfs.xShmOpen = cfShmOpen; - } sqlite3_vfs_register(&crashVfs, 0); }else{ crashVfs.pAppData = 0; diff --git a/src/test_devsym.c b/src/test_devsym.c index 082d101d4..ed7078d7a 100644 --- a/src/test_devsym.c +++ b/src/test_devsym.c @@ -68,13 +68,6 @@ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); -static int devsymShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **); -static int devsymShmSize(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int devsymShmGet(sqlite3_vfs*, sqlite3_shm *, int , int *, void **); -static int devsymShmRelease(sqlite3_vfs*, sqlite3_shm *); -static int devsymShmLock(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int devsymShmClose(sqlite3_vfs*, sqlite3_shm *, int); - static sqlite3_vfs devsym_vfs = { 2, /* iVersion */ sizeof(devsym_file), /* szOsFile */ @@ -101,18 +94,12 @@ static sqlite3_vfs devsym_vfs = { devsymSleep, /* xSleep */ devsymCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ - devsymShmOpen, - devsymShmSize, - devsymShmGet, - devsymShmRelease, - devsymShmLock, - devsymShmClose, - 0, - 0, + 0, /* xRename */ + 0 /* xCurrentTimeInt64 */ }; static sqlite3_io_methods devsym_io_methods = { - 1, /* iVersion */ + 2, /* iVersion */ devsymClose, /* xClose */ devsymRead, /* xRead */ devsymWrite, /* xWrite */ @@ -124,7 +111,13 @@ static sqlite3_io_methods devsym_io_methods = { devsymCheckReservedLock, /* xCheckReservedLock */ devsymFileControl, /* xFileControl */ devsymSectorSize, /* xSectorSize */ - devsymDeviceCharacteristics /* xDeviceCharacteristics */ + devsymDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmOpen */ + 0, /* xShmSize */ + 0, /* xShmGet */ + 0, /* xShmRelease */ + 0, /* xShmLock */ + 0 /* xShmClose */ }; struct DevsymGlobal { @@ -350,45 +343,6 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ } -static int devsymShmOpen( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_shm **pp -){ - return g.pVfs->xShmOpen(g.pVfs, zName, pp); -} -static int devsymShmSize( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int reqSize, - int *pNewSize -){ - return g.pVfs->xShmSize(g.pVfs, p, reqSize, pNewSize); -} -static int devsymShmGet( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int reqMapSize, - int *pMapSize, - void **pp -){ - return g.pVfs->xShmGet(g.pVfs, p, reqMapSize, pMapSize, pp); -} -static int devsymShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *p){ - return g.pVfs->xShmRelease(g.pVfs, p); -} -static int devsymShmLock( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int desiredLock, - int *gotLock -){ - return g.pVfs->xShmLock(g.pVfs, p, desiredLock, gotLock); -} -static int devsymShmClose(sqlite3_vfs *pVfs, sqlite3_shm *p, int deleteFlag){ - return g.pVfs->xShmClose(g.pVfs, p, deleteFlag); -} - /* ** This procedure registers the devsym vfs with SQLite. If the argument is ** true, the devsym vfs becomes the new default vfs. It is the only publicly @@ -398,12 +352,6 @@ void devsym_register(int iDeviceChar, int iSectorSize){ if( g.pVfs==0 ){ g.pVfs = sqlite3_vfs_find(0); devsym_vfs.szOsFile += g.pVfs->szOsFile; - devsym_vfs.xShmOpen = (g.pVfs->xShmOpen ? devsymShmOpen : 0); - devsym_vfs.xShmSize = (g.pVfs->xShmSize ? devsymShmSize : 0); - devsym_vfs.xShmGet = (g.pVfs->xShmGet ? devsymShmGet : 0); - devsym_vfs.xShmRelease = (g.pVfs->xShmRelease ? devsymShmRelease : 0); - devsym_vfs.xShmLock = (g.pVfs->xShmLock ? devsymShmLock : 0); - devsym_vfs.xShmClose = (g.pVfs->xShmClose ? devsymShmClose : 0); sqlite3_vfs_register(&devsym_vfs, 0); } if( iDeviceChar>=0 ){ diff --git a/src/test_onefile.c b/src/test_onefile.c index 6b39a39ad..520dffcb7 100644 --- a/src/test_onefile.c +++ b/src/test_onefile.c @@ -199,11 +199,6 @@ static fs_vfs_t fs_vfs = { fsRandomness, /* xRandomness */ fsSleep, /* xSleep */ fsCurrentTime, /* xCurrentTime */ - 0, /* xShmOpen */ - 0, /* xShmSize */ - 0, /* xShmLock */ - 0, /* xShmClose */ - 0, /* xShmDelete */ 0, /* xRename */ 0 /* xCurrentTimeInt64 */ }, @@ -224,7 +219,12 @@ static sqlite3_io_methods fs_io_methods = { fsCheckReservedLock, /* xCheckReservedLock */ fsFileControl, /* xFileControl */ fsSectorSize, /* xSectorSize */ - fsDeviceCharacteristics /* xDeviceCharacteristics */ + fsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmOpen */ + 0, /* xShmSize */ + 0, /* xShmLock */ + 0, /* xShmClose */ + 0, /* xShmDelete */ }; @@ -241,7 +241,12 @@ static sqlite3_io_methods tmp_io_methods = { tmpCheckReservedLock, /* xCheckReservedLock */ tmpFileControl, /* xFileControl */ tmpSectorSize, /* xSectorSize */ - tmpDeviceCharacteristics /* xDeviceCharacteristics */ + tmpDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmOpen */ + 0, /* xShmSize */ + 0, /* xShmLock */ + 0, /* xShmClose */ + 0, /* xShmDelete */ }; /* Useful macros used in several places */ diff --git a/src/test_vfs.c b/src/test_vfs.c index 1b5b4b09d..af0c24b49 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -16,15 +16,20 @@ #include "sqlite3.h" #include "sqliteInt.h" -typedef struct tvfs_file tvfs_file; -struct tvfs_file { - sqlite3_file base; - sqlite3_file *pReal; -}; +#if 0 /* FIX THIS LATER */ typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; typedef struct TestvfsBuffer TestvfsBuffer; +typedef struct tvfs_file tvfs_file; +struct tvfs_file { + sqlite3_file base; /* Base class. Must be first */ + sqlite3_vfs *pVfs; /* the VFS */ + TestvfsShm *pShm; /* Shared memory segment */ + const char *zFilename; /* Filename */ + sqlite3_file *pReal; /* The real, underlying file descriptor */ +}; + /* ** An instance of this structure is allocated for each VFS created. The @@ -97,15 +102,15 @@ static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); static int tvfsSleep(sqlite3_vfs*, int microseconds); static int tvfsCurrentTime(sqlite3_vfs*, double*); -static int tvfsShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **); -static int tvfsShmSize(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int tvfsShmGet(sqlite3_vfs*, sqlite3_shm *, int , int *, void **); -static int tvfsShmRelease(sqlite3_vfs*, sqlite3_shm *); -static int tvfsShmLock(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int tvfsShmClose(sqlite3_vfs*, sqlite3_shm *, int); +static int tvfsShmOpen(sqlite3_file*); +static int tvfsShmSize(sqlite3_file*, int , int *); +static int tvfsShmGet(sqlite3_file*, int , int *, void **); +static int tvfsShmRelease(sqlite3_file*); +static int tvfsShmLock(sqlite3_file*, int , int *); +static int tvfsShmClose(sqlite3_file*, int); static sqlite3_io_methods tvfs_io_methods = { - 1, /* iVersion */ + 2, /* iVersion */ tvfsClose, /* xClose */ tvfsRead, /* xRead */ tvfsWrite, /* xWrite */ @@ -117,7 +122,13 @@ static sqlite3_io_methods tvfs_io_methods = { tvfsCheckReservedLock, /* xCheckReservedLock */ tvfsFileControl, /* xFileControl */ tvfsSectorSize, /* xSectorSize */ - tvfsDeviceCharacteristics /* xDeviceCharacteristics */ + tvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + tvfsShmOpen, /* xShmOpen */ + tvfsShmSize, /* xShmSize */ + tvfsShmGet, /* xShmGet */ + tvfsShmRelease, /* xShmRelease */ + tvfsShmLock, /* xShmLock */ + tvfsShmClose /* xShmClose */ }; /* @@ -238,6 +249,9 @@ static int tvfsOpen( ){ int rc; tvfs_file *p = (tvfs_file *)pFile; + p->pShm = 0; + p->zFilename = zName; + p->pVfs = pVfs; p->pReal = (sqlite3_file *)&p[1]; rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, p->pReal, flags, pOutFlags); if( p->pReal->pMethods ){ @@ -405,15 +419,16 @@ static int tvfsResultCode(Testvfs *p, int *pRc){ } static int tvfsShmOpen( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_shm **pp + sqlite3_file *pFileDes ){ Testvfs *p = (Testvfs *)(pVfs->pAppData); int rc = SQLITE_OK; /* Return code */ Tcl_Obj *pId = 0; /* Id for this connection */ TestvfsBuffer *pBuffer; /* Buffer to open connection to */ TestvfsShm *pShm; /* New shm handle */ + tvfs_file *pFd; /* The file descriptor */ + + pFd = (tvfs_file*)pFileDes; /* Evaluate the Tcl script: ** @@ -424,7 +439,7 @@ static int tvfsShmOpen( ** connection is named "anon". Otherwise, the value returned by the ** script is used as the connection name. */ - tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(zName, -1), 0, 0); + tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; pId = Tcl_NewStringObj("anon", -1); @@ -714,12 +729,6 @@ static int testvfs_cmd( tvfsSleep, /* xSleep */ tvfsCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ - tvfsShmOpen, - tvfsShmSize, - tvfsShmGet, - tvfsShmRelease, - tvfsShmLock, - tvfsShmClose, 0, 0, }; @@ -788,9 +797,10 @@ static int testvfs_cmd( Tcl_WrongNumArgs(interp, 1, objv, "?-noshm? VFSNAME SCRIPT"); return TCL_ERROR; } +#endif /* 0 */ int Sqlitetestvfs_Init(Tcl_Interp *interp){ - Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); + /* Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); */ return TCL_OK; } diff --git a/src/vdbe.c b/src/vdbe.c index 88f880ea9..3fee165da 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5248,8 +5248,8 @@ case OP_JournalMode: { /* out2-prerelease */ ** in temporary storage or if the VFS does not support xShmOpen. */ if( eNew==PAGER_JOURNALMODE_WAL - && (zFilename[0]==0 /* Temp file */ - || pVfs->iVersion<2 || pVfs->xShmOpen==0) /* No xShmOpen support */ + && (zFilename[0]==0 /* Temp file */ + || !sqlite3PagerWalSupported(pPager)) /* No xShmOpen support */ ){ eNew = PAGER_JOURNALMODE_QUERY; } diff --git a/src/wal.c b/src/wal.c index bdae2e9df..0b2c3ceb6 100644 --- a/src/wal.c +++ b/src/wal.c @@ -125,16 +125,17 @@ struct WalIndexHdr { */ struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pFd */ - sqlite3_file *pFd; /* File handle for WAL file */ + sqlite3_file *pDbFd; /* File handle for the database file */ + sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ - sqlite3_shm *pWIndex; /* The open wal-index file */ int szWIndex; /* Size of the wal-index that is mapped in mem */ u32 *pWiData; /* Pointer to wal-index content in memory */ u8 lockState; /* SQLITE_SHM_xxxx constant showing lock state */ u8 readerType; /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ + u8 isWindexOpen; /* True if ShmOpen() called on pDbFd */ WalIndexHdr hdr; /* Wal-index for current snapshot */ - char *zName; /* Name of underlying storage */ + char *zWalName; /* Name of WAL file */ }; @@ -223,7 +224,7 @@ static int walSetLock(Wal *pWal, int desiredStatus){ pWal->lockState = desiredStatus; }else{ int got = pWal->lockState; - rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got); + rc = sqlite3OsShmLock(pWal->pWalFd, desiredStatus, &got); pWal->lockState = got; if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){ pWal->readerType = got; @@ -404,7 +405,7 @@ static int walMappingSize(u32 iFrame){ */ static void walIndexUnmap(Wal *pWal){ if( pWal->pWiData ){ - pWal->pVfs->xShmRelease(pWal->pVfs, pWal->pWIndex); + sqlite3OsShmRelease(pWal->pDbFd); pWal->pWiData = 0; } } @@ -418,8 +419,8 @@ static void walIndexUnmap(Wal *pWal){ static int walIndexMap(Wal *pWal, int reqSize){ int rc = SQLITE_OK; if( pWal->pWiData==0 || reqSize>pWal->szWIndex ){ - rc = pWal->pVfs->xShmGet(pWal->pVfs, pWal->pWIndex, reqSize, - &pWal->szWIndex, (void**)(char*)&pWal->pWiData); + rc = sqlite3OsShmGet(pWal->pDbFd, reqSize, &pWal->szWIndex, + (void**)(char*)&pWal->pWiData); if( rc==SQLITE_OK && pWal->pWiData==0 ){ /* Make sure pWal->pWiData is not NULL while we are holding the ** lock on the mapping. */ @@ -443,7 +444,7 @@ static int walIndexMap(Wal *pWal, int reqSize){ static int walIndexRemap(Wal *pWal, int enlargeTo){ int rc; int sz; - rc = pWal->pVfs->xShmSize(pWal->pVfs, pWal->pWIndex, enlargeTo, &sz); + rc = sqlite3OsShmSize(pWal->pDbFd, enlargeTo, &sz); if( rc==SQLITE_OK && sz>pWal->szWIndex ){ walIndexUnmap(pWal); rc = walIndexMap(pWal, sz); @@ -561,7 +562,7 @@ static int walIndexRecover(Wal *pWal){ assert( pWal->lockState>SQLITE_SHM_READ ); memset(&hdr, 0, sizeof(hdr)); - rc = sqlite3OsFileSize(pWal->pFd, &nSize); + rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ return rc; } @@ -579,7 +580,7 @@ static int walIndexRecover(Wal *pWal){ /* Read in the first frame header in the file (to determine the ** database page size). */ - rc = sqlite3OsRead(pWal->pFd, aBuf, WAL_HDRSIZE, 0); + rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -610,7 +611,7 @@ static int walIndexRecover(Wal *pWal){ int isValid; /* True if this frame is valid */ /* Read and decode the next log frame. */ - rc = sqlite3OsRead(pWal->pFd, aFrame, nFrame, iOffset); + rc = sqlite3OsRead(pWal->pWalFd, aFrame, nFrame, iOffset); if( rc!=SQLITE_OK ) break; isValid = walDecodeFrame(aCksum, &pgno, &nTruncate, nPgsz, aData, aFrame); if( !isValid ) break; @@ -648,12 +649,11 @@ static int walIndexRecover(Wal *pWal){ ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ - sqlite3_shm *pWIndex = pWal->pWIndex; - if( pWIndex ){ - sqlite3_vfs *pVfs = pWal->pVfs; + if( pWal->isWindexOpen ){ int notUsed; - pVfs->xShmLock(pVfs, pWIndex, SQLITE_SHM_UNLOCK, ¬Used); - pVfs->xShmClose(pVfs, pWIndex, isDelete); + sqlite3OsShmLock(pWal->pDbFd, SQLITE_SHM_UNLOCK, ¬Used); + sqlite3OsShmClose(pWal->pDbFd, isDelete); + pWal->isWindexOpen = 0; } } @@ -675,41 +675,44 @@ static void walIndexClose(Wal *pWal, int isDelete){ */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ - const char *zDb, /* Name of database file */ + sqlite3_file *pDbFd, /* The open database file */ + const char *zDbName, /* Name of the database file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ - char *zWal; /* Path to WAL file */ + char *zWal; /* Name of write-ahead log file */ int nWal; /* Length of zWal in bytes */ - assert( zDb ); - if( pVfs->xShmOpen==0 ) return SQLITE_CANTOPEN_BKPT; + assert( zDbName && zDbName[0] ); + assert( pDbFd ); /* Allocate an instance of struct Wal to return. */ *ppWal = 0; - nWal = strlen(zDb); - pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile + nWal+5); + nWal = sqlite3Strlen30(zWal) + 5; + pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile + nWal); if( !pRet ){ return SQLITE_NOMEM; } pRet->pVfs = pVfs; - pRet->pFd = (sqlite3_file *)&pRet[1]; - pRet->zName = zWal = pVfs->szOsFile + (char*)pRet->pFd; - sqlite3_snprintf(nWal+5, zWal, "%s-wal", zDb); - rc = pVfs->xShmOpen(pVfs, zDb, &pRet->pWIndex); + pRet->pWalFd = (sqlite3_file *)&pRet[1]; + pRet->pDbFd = pDbFd; + pRet->zWalName = zWal = pVfs->szOsFile + (char*)pRet->pWalFd; + sqlite3_snprintf(nWal, zWal, "%s-wal", zDbName); + rc = sqlite3OsShmOpen(pDbFd); + pRet->isWindexOpen = 1; /* Open file handle on the write-ahead log file. */ if( rc==SQLITE_OK ){ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_JOURNAL); - rc = sqlite3OsOpen(pVfs, zWal, pRet->pFd, flags, &flags); + rc = sqlite3OsOpen(pVfs, zWal, pRet->pWalFd, flags, &flags); } if( rc!=SQLITE_OK ){ walIndexClose(pRet, 0); - sqlite3OsClose(pRet->pFd); + sqlite3OsClose(pRet->pWalFd); sqlite3_free(pRet); }else{ *ppWal = pRet; @@ -809,7 +812,6 @@ static void walIteratorFree(WalIterator *p){ */ static int walCheckpoint( Wal *pWal, /* Wal connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags for OsSync() (or 0) */ int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ @@ -833,27 +835,27 @@ static int walCheckpoint( /* Sync the log file to disk */ if( sync_flags ){ - rc = sqlite3OsSync(pWal->pFd, sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); if( rc!=SQLITE_OK ) goto out; } /* Iterate through the contents of the log, copying data to the db file. */ while( 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - rc = sqlite3OsRead(pWal->pFd, zBuf, pgsz, + rc = sqlite3OsRead(pWal->pWalFd, zBuf, pgsz, walFrameOffset(iFrame, pgsz) + WAL_FRAME_HDRSIZE ); if( rc!=SQLITE_OK ) goto out; - rc = sqlite3OsWrite(pFd, zBuf, pgsz, (iDbpage-1)*pgsz); + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, pgsz, (iDbpage-1)*pgsz); if( rc!=SQLITE_OK ) goto out; } /* Truncate the database file */ - rc = sqlite3OsTruncate(pFd, ((i64)pWal->hdr.nPage*(i64)pgsz)); + rc = sqlite3OsTruncate(pWal->pDbFd, ((i64)pWal->hdr.nPage*(i64)pgsz)); if( rc!=SQLITE_OK ) goto out; /* Sync the database file. If successful, update the wal-index. */ if( sync_flags ){ - rc = sqlite3OsSync(pFd, sync_flags); + rc = sqlite3OsSync(pWal->pDbFd, sync_flags); if( rc!=SQLITE_OK ) goto out; } pWal->hdr.iLastPg = 0; @@ -876,9 +878,9 @@ static int walCheckpoint( */ #if 0 memset(zBuf, 0, WAL_FRAME_HDRSIZE); - rc = sqlite3OsWrite(pWal->pFd, zBuf, WAL_FRAME_HDRSIZE, 0); + rc = sqlite3OsWrite(pWal->pWalFd, zBuf, WAL_FRAME_HDRSIZE, 0); if( rc!=SQLITE_OK ) goto out; - rc = sqlite3OsSync(pWal->pFd, pWal->sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, pWal->sync_flags); #endif out: @@ -891,7 +893,6 @@ static int walCheckpoint( */ int sqlite3WalClose( Wal *pWal, /* Wal to close */ - sqlite3_file *pFd, /* Database file */ int sync_flags, /* Flags to pass to OsSync() (or 0) */ int nBuf, u8 *zBuf /* Buffer of at least nBuf bytes */ @@ -908,9 +909,9 @@ int sqlite3WalClose( ** ** The EXCLUSIVE lock is not released before returning. */ - rc = sqlite3OsLock(pFd, SQLITE_LOCK_EXCLUSIVE); + rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ - rc = sqlite3WalCheckpoint(pWal, pFd, sync_flags, nBuf, zBuf, 0, 0); + rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf, 0, 0); if( rc==SQLITE_OK ){ isDelete = 1; } @@ -918,9 +919,9 @@ int sqlite3WalClose( } walIndexClose(pWal, isDelete); - sqlite3OsClose(pWal->pFd); + sqlite3OsClose(pWal->pWalFd); if( isDelete ){ - sqlite3OsDelete(pWal->pVfs, pWal->zName, 0); + sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); } sqlite3_free(pWal); } @@ -1191,7 +1192,7 @@ int sqlite3WalRead( if( iRead ){ i64 iOffset = walFrameOffset(iRead, pWal->hdr.pgsz) + WAL_FRAME_HDRSIZE; *pInWal = 1; - return sqlite3OsRead(pWal->pFd, pOut, nOut, iOffset); + return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset); } *pInWal = 0; @@ -1290,7 +1291,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 iFrame){ pWal->hdr.iLastPg = iFrame; if( iFrame>0 ){ i64 iOffset = walFrameOffset(iFrame, pWal->hdr.pgsz) + sizeof(u32)*2; - rc = sqlite3OsRead(pWal->pFd, aCksum, sizeof(aCksum), iOffset); + rc = sqlite3OsRead(pWal->pWalFd, aCksum, sizeof(aCksum), iOffset); pWal->hdr.iCheck1 = sqlite3Get4byte(&aCksum[0]); pWal->hdr.iCheck2 = sqlite3Get4byte(&aCksum[4]); } @@ -1334,7 +1335,7 @@ int sqlite3WalFrames( sqlite3_randomness(8, &aFrame[4]); pWal->hdr.iCheck1 = sqlite3Get4byte(&aFrame[4]); pWal->hdr.iCheck2 = sqlite3Get4byte(&aFrame[8]); - rc = sqlite3OsWrite(pWal->pFd, aFrame, WAL_HDRSIZE, 0); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -1353,13 +1354,13 @@ int sqlite3WalFrames( /* Populate and write the frame header */ nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0; walEncodeFrame(aCksum, p->pgno, nDbsize, nPgsz, p->pData, aFrame); - rc = sqlite3OsWrite(pWal->pFd, aFrame, sizeof(aFrame), iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } /* Write the page data */ - rc = sqlite3OsWrite(pWal->pFd, p->pData, nPgsz, iOffset + sizeof(aFrame)); + rc = sqlite3OsWrite(pWal->pWalFd, p->pData, nPgsz, iOffset + sizeof(aFrame)); if( rc!=SQLITE_OK ){ return rc; } @@ -1368,7 +1369,7 @@ int sqlite3WalFrames( /* Sync the log file if the 'isSync' flag was specified. */ if( sync_flags ){ - i64 iSegment = sqlite3OsSectorSize(pWal->pFd); + i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd); i64 iOffset = walFrameOffset(iFrame+1, nPgsz); assert( isCommit ); @@ -1379,13 +1380,13 @@ int sqlite3WalFrames( iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment); while( iOffsetpgno,nTruncate,nPgsz,pLast->pData,aFrame); - rc = sqlite3OsWrite(pWal->pFd, aFrame, sizeof(aFrame), iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } iOffset += WAL_FRAME_HDRSIZE; - rc = sqlite3OsWrite(pWal->pFd, pLast->pData, nPgsz, iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, pLast->pData, nPgsz, iOffset); if( rc!=SQLITE_OK ){ return rc; } @@ -1393,7 +1394,7 @@ int sqlite3WalFrames( iOffset += nPgsz; } - rc = sqlite3OsSync(pWal->pFd, sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } assert( pWal->pWiData==0 ); @@ -1445,7 +1446,6 @@ int sqlite3WalFrames( */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ u8 *zBuf, /* Temporary buffer to use */ @@ -1479,7 +1479,7 @@ int sqlite3WalCheckpoint( /* Copy data from the log to the database file. */ rc = walIndexReadHdr(pWal, &isChanged); if( rc==SQLITE_OK ){ - rc = walCheckpoint(pWal, pFd, sync_flags, nBuf, zBuf); + rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); } if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was diff --git a/src/wal.h b/src/wal.h index 89e94869c..1e25fc811 100644 --- a/src/wal.h +++ b/src/wal.h @@ -41,8 +41,8 @@ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ -int sqlite3WalOpen(sqlite3_vfs*, const char *zDb, Wal **ppWal); -int sqlite3WalClose(Wal *pWal, sqlite3_file *pFd, int sync_flags, int, u8 *); +int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**); +int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database @@ -81,7 +81,6 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ u8 *zBuf, /* Temporary buffer to use */