| Index: third_party/sqlite/src/src/os_unix.c
|
| diff --git a/third_party/sqlite/src/src/os_unix.c b/third_party/sqlite/src/src/os_unix.c
|
| index e5e1509c1c5ca20af4a4125341e7ba683ee23589..22164702ecd1e6fef5a447e0c526d253d6b8e65d 100644
|
| --- a/third_party/sqlite/src/src/os_unix.c
|
| +++ b/third_party/sqlite/src/src/os_unix.c
|
| @@ -119,6 +119,9 @@
|
| #include <time.h>
|
| #include <sys/time.h>
|
| #include <errno.h>
|
| +#ifndef SQLITE_OMIT_WAL
|
| +#include <sys/mman.h>
|
| +#endif
|
|
|
| #if SQLITE_ENABLE_LOCKING_STYLE
|
| # include <sys/ioctl.h>
|
| @@ -128,10 +131,18 @@
|
| # else
|
| # include <sys/file.h>
|
| # include <sys/param.h>
|
| -# include <sys/mount.h>
|
| # endif
|
| #endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
|
|
| +#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
|
| +# include <sys/mount.h>
|
| +#endif
|
| +
|
| +/*
|
| +** Allowed values of unixFile.fsFlags
|
| +*/
|
| +#define SQLITE_FSFLAGS_IS_MSDOS 0x1
|
| +
|
| /*
|
| ** If we are to be thread-safe, include the pthreads header and define
|
| ** the SQLITE_UNIX_THREADS macro.
|
| @@ -166,6 +177,11 @@
|
| */
|
| #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))
|
|
|
| +/* Forward references */
|
| +typedef struct unixShm unixShm; /* Connection shared memory */
|
| +typedef struct unixShmNode unixShmNode; /* Shared memory instance */
|
| +typedef struct unixInodeInfo unixInodeInfo; /* An i-node */
|
| +typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */
|
|
|
| /*
|
| ** Sometimes, after a file handle is closed by SQLite, the file descriptor
|
| @@ -173,7 +189,6 @@
|
| ** structure are used to store the file descriptor while waiting for an
|
| ** opportunity to either close or reuse it.
|
| */
|
| -typedef struct UnixUnusedFd UnixUnusedFd;
|
| struct UnixUnusedFd {
|
| int fd; /* File descriptor to close */
|
| int flags; /* Flags this file descriptor was opened with */
|
| @@ -187,24 +202,26 @@ struct UnixUnusedFd {
|
| typedef struct unixFile unixFile;
|
| struct unixFile {
|
| sqlite3_io_methods const *pMethod; /* Always the first entry */
|
| - struct unixOpenCnt *pOpen; /* Info about all open fd's on this inode */
|
| - struct unixLockInfo *pLock; /* Info about locks on this inode */
|
| - int h; /* The file descriptor */
|
| - int dirfd; /* File descriptor for the directory */
|
| - unsigned char locktype; /* The type of lock held on this fd */
|
| - int lastErrno; /* The unix errno from the last I/O error */
|
| - void *lockingContext; /* Locking style specific state */
|
| - UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
|
| - int fileFlags; /* Miscellanous flags */
|
| + unixInodeInfo *pInode; /* Info about locks on this inode */
|
| + int h; /* The file descriptor */
|
| + int dirfd; /* File descriptor for the directory */
|
| + unsigned char eFileLock; /* The type of lock held on this fd */
|
| + unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
|
| + int lastErrno; /* The unix errno from last I/O error */
|
| + void *lockingContext; /* Locking style specific state */
|
| + UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
|
| + const char *zPath; /* Name of the file */
|
| + unixShm *pShm; /* Shared memory segment information */
|
| + int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
|
| #if SQLITE_ENABLE_LOCKING_STYLE
|
| - int openFlags; /* The flags specified at open() */
|
| + int openFlags; /* The flags specified at open() */
|
| #endif
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| - pthread_t tid; /* The thread that "owns" this unixFile */
|
| +#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
|
| + unsigned fsFlags; /* cached details from statfs() */
|
| #endif
|
| #if OS_VXWORKS
|
| - int isDelete; /* Delete on close if true */
|
| - struct vxworksFileId *pId; /* Unique file ID */
|
| + int isDelete; /* Delete on close if true */
|
| + struct vxworksFileId *pId; /* Unique file ID */
|
| #endif
|
| #ifndef NDEBUG
|
| /* The next group of variables are used to track whether or not the
|
| @@ -227,9 +244,10 @@ struct unixFile {
|
| };
|
|
|
| /*
|
| -** The following macros define bits in unixFile.fileFlags
|
| +** Allowed values for the unixFile.ctrlFlags bitmask:
|
| */
|
| -#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */
|
| +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
|
| +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
|
|
|
| /*
|
| ** Include code that is common to all os_*.c files
|
| @@ -254,29 +272,208 @@ struct unixFile {
|
| #endif
|
|
|
| /*
|
| +** The threadid macro resolves to the thread-id or to 0. Used for
|
| +** testing and debugging only.
|
| +*/
|
| +#if SQLITE_THREADSAFE
|
| +#define threadid pthread_self()
|
| +#else
|
| +#define threadid 0
|
| +#endif
|
| +
|
| +/*
|
| +** Many system calls are accessed through pointer-to-functions so that
|
| +** they may be overridden at runtime to facilitate fault injection during
|
| +** testing and sandboxing. The following array holds the names and pointers
|
| +** to all overrideable system calls.
|
| +*/
|
| +static struct unix_syscall {
|
| + const char *zName; /* Name of the sytem call */
|
| + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
| + sqlite3_syscall_ptr pDefault; /* Default value */
|
| +} aSyscall[] = {
|
| + { "open", (sqlite3_syscall_ptr)open, 0 },
|
| +#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent)
|
| +
|
| + { "close", (sqlite3_syscall_ptr)close, 0 },
|
| +#define osClose ((int(*)(int))aSyscall[1].pCurrent)
|
| +
|
| + { "access", (sqlite3_syscall_ptr)access, 0 },
|
| +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent)
|
| +
|
| + { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 },
|
| +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent)
|
| +
|
| + { "stat", (sqlite3_syscall_ptr)stat, 0 },
|
| +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent)
|
| +
|
| +/*
|
| ** The DJGPP compiler environment looks mostly like Unix, but it
|
| ** lacks the fcntl() system call. So redefine fcntl() to be something
|
| ** that always succeeds. This means that locking does not occur under
|
| ** DJGPP. But it is DOS - what did you expect?
|
| */
|
| #ifdef __DJGPP__
|
| -# define fcntl(A,B,C) 0
|
| + { "fstat", 0, 0 },
|
| +#define osFstat(a,b,c) 0
|
| +#else
|
| + { "fstat", (sqlite3_syscall_ptr)fstat, 0 },
|
| +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent)
|
| #endif
|
|
|
| -/*
|
| -** The threadid macro resolves to the thread-id or to 0. Used for
|
| -** testing and debugging only.
|
| -*/
|
| -#if SQLITE_THREADSAFE
|
| -#define threadid pthread_self()
|
| + { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 },
|
| +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent)
|
| +
|
| + { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 },
|
| +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent)
|
| +
|
| + { "read", (sqlite3_syscall_ptr)read, 0 },
|
| +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
|
| +
|
| +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
|
| + { "pread", (sqlite3_syscall_ptr)pread, 0 },
|
| #else
|
| -#define threadid 0
|
| + { "pread", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent)
|
| +
|
| +#if defined(USE_PREAD64)
|
| + { "pread64", (sqlite3_syscall_ptr)pread64, 0 },
|
| +#else
|
| + { "pread64", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent)
|
| +
|
| + { "write", (sqlite3_syscall_ptr)write, 0 },
|
| +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
|
| +
|
| +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE)
|
| + { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
|
| +#else
|
| + { "pwrite", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
|
| + aSyscall[12].pCurrent)
|
| +
|
| +#if defined(USE_PREAD64)
|
| + { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 },
|
| +#else
|
| + { "pwrite64", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
|
| + aSyscall[13].pCurrent)
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
|
| +#else
|
| + { "fchmod", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
|
| +
|
| +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
|
| + { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
|
| +#else
|
| + { "fallocate", (sqlite3_syscall_ptr)0, 0 },
|
| #endif
|
| +#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
|
| +
|
| +}; /* End of the overrideable system calls */
|
| +
|
| +/*
|
| +** This is the xSetSystemCall() method of sqlite3_vfs for all of the
|
| +** "unix" VFSes. Return SQLITE_OK opon successfully updating the
|
| +** system call pointer, or SQLITE_NOTFOUND if there is no configurable
|
| +** system call named zName.
|
| +*/
|
| +static int unixSetSystemCall(
|
| + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
|
| + const char *zName, /* Name of system call to override */
|
| + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
|
| +){
|
| + unsigned int i;
|
| + int rc = SQLITE_NOTFOUND;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + if( zName==0 ){
|
| + /* If no zName is given, restore all system calls to their default
|
| + ** settings and return NULL
|
| + */
|
| + rc = SQLITE_OK;
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( aSyscall[i].pDefault ){
|
| + aSyscall[i].pCurrent = aSyscall[i].pDefault;
|
| + }
|
| + }
|
| + }else{
|
| + /* If zName is specified, operate on only the one system call
|
| + ** specified.
|
| + */
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ){
|
| + if( aSyscall[i].pDefault==0 ){
|
| + aSyscall[i].pDefault = aSyscall[i].pCurrent;
|
| + }
|
| + rc = SQLITE_OK;
|
| + if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
|
| + aSyscall[i].pCurrent = pNewFunc;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return the value of a system call. Return NULL if zName is not a
|
| +** recognized system call name. NULL is also returned if the system call
|
| +** is currently undefined.
|
| +*/
|
| +static sqlite3_syscall_ptr unixGetSystemCall(
|
| + sqlite3_vfs *pNotUsed,
|
| + const char *zName
|
| +){
|
| + unsigned int i;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Return the name of the first system call after zName. If zName==NULL
|
| +** then return the name of the first system call. Return NULL if zName
|
| +** is the last system call or if zName is not the name of a valid
|
| +** system call.
|
| +*/
|
| +static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
|
| + int i = -1;
|
| +
|
| + UNUSED_PARAMETER(p);
|
| + if( zName ){
|
| + for(i=0; i<ArraySize(aSyscall)-1; i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) break;
|
| + }
|
| + }
|
| + for(i++; i<ArraySize(aSyscall); i++){
|
| + if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
|
| + }
|
| + return 0;
|
| +}
|
|
|
| +/*
|
| +** Retry open() calls that fail due to EINTR
|
| +*/
|
| +static int robust_open(const char *z, int f, int m){
|
| + int rc;
|
| + do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR );
|
| + return rc;
|
| +}
|
|
|
| /*
|
| ** Helper functions to obtain and relinquish the global mutex. The
|
| -** global mutex is used to protect the unixOpenCnt, unixLockInfo and
|
| +** global mutex is used to protect the unixInodeInfo and
|
| ** vxworksFileId objects used by this file, all of which may be
|
| ** shared by multiple threads.
|
| **
|
| @@ -307,8 +504,8 @@ static int unixMutexHeld(void) {
|
| ** binaries. This returns the string represetation of the supplied
|
| ** integer lock-type.
|
| */
|
| -static const char *locktypeName(int locktype){
|
| - switch( locktype ){
|
| +static const char *azFileLock(int eFileLock){
|
| + switch( eFileLock ){
|
| case NO_LOCK: return "NONE";
|
| case SHARED_LOCK: return "SHARED";
|
| case RESERVED_LOCK: return "RESERVED";
|
| @@ -337,7 +534,7 @@ static int lockTrace(int fd, int op, struct flock *p){
|
| }else if( op==F_SETLK ){
|
| zOpName = "SETLK";
|
| }else{
|
| - s = fcntl(fd, op, p);
|
| + s = osFcntl(fd, op, p);
|
| sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s);
|
| return s;
|
| }
|
| @@ -351,7 +548,7 @@ static int lockTrace(int fd, int op, struct flock *p){
|
| assert( 0 );
|
| }
|
| assert( p->l_whence==SEEK_SET );
|
| - s = fcntl(fd, op, p);
|
| + s = osFcntl(fd, op, p);
|
| savedErrno = errno;
|
| sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n",
|
| threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len,
|
| @@ -359,7 +556,7 @@ static int lockTrace(int fd, int op, struct flock *p){
|
| if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){
|
| struct flock l2;
|
| l2 = *p;
|
| - fcntl(fd, F_GETLK, &l2);
|
| + osFcntl(fd, F_GETLK, &l2);
|
| if( l2.l_type==F_RDLCK ){
|
| zType = "RDLCK";
|
| }else if( l2.l_type==F_WRLCK ){
|
| @@ -375,10 +572,18 @@ static int lockTrace(int fd, int op, struct flock *p){
|
| errno = savedErrno;
|
| return s;
|
| }
|
| -#define fcntl lockTrace
|
| +#undef osFcntl
|
| +#define osFcntl lockTrace
|
| #endif /* SQLITE_LOCK_TRACE */
|
|
|
| -
|
| +/*
|
| +** Retry ftruncate() calls that fail due to EINTR
|
| +*/
|
| +static int robust_ftruncate(int h, sqlite3_int64 sz){
|
| + int rc;
|
| + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR );
|
| + return rc;
|
| +}
|
|
|
| /*
|
| ** This routine translates a standard POSIX errno code into something
|
| @@ -392,9 +597,22 @@ static int lockTrace(int fd, int op, struct flock *p){
|
| */
|
| static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
| switch (posixError) {
|
| +#if 0
|
| + /* At one point this code was not commented out. In theory, this branch
|
| + ** should never be hit, as this function should only be called after
|
| + ** a locking-related function (i.e. fcntl()) has returned non-zero with
|
| + ** the value of errno as the first argument. Since a system call has failed,
|
| + ** errno should be non-zero.
|
| + **
|
| + ** Despite this, if errno really is zero, we still don't want to return
|
| + ** SQLITE_OK. The system call failed, and *some* SQLite error should be
|
| + ** propagated back to the caller. Commenting this branch out means errno==0
|
| + ** will be handled by the "default:" case below.
|
| + */
|
| case 0:
|
| return SQLITE_OK;
|
| -
|
| +#endif
|
| +
|
| case EAGAIN:
|
| case ETIMEDOUT:
|
| case EBUSY:
|
| @@ -416,8 +634,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
| case EPERM:
|
| return SQLITE_PERM;
|
|
|
| + /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
|
| + ** this module never makes such a call. And the code in SQLite itself
|
| + ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
|
| + ** this case is also commented out. If the system does set errno to EDEADLK,
|
| + ** the default SQLITE_IOERR_XXX code will be returned. */
|
| +#if 0
|
| case EDEADLK:
|
| return SQLITE_IOERR_BLOCKED;
|
| +#endif
|
|
|
| #if EOPNOTSUPP!=ENOTSUP
|
| case EOPNOTSUPP:
|
| @@ -645,13 +870,12 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
|
| **
|
| ** If you close a file descriptor that points to a file that has locks,
|
| ** all locks on that file that are owned by the current process are
|
| -** released. To work around this problem, each unixFile structure contains
|
| -** a pointer to an unixOpenCnt structure. There is one unixOpenCnt structure
|
| -** per open inode, which means that multiple unixFile can point to a single
|
| -** unixOpenCnt. When an attempt is made to close an unixFile, if there are
|
| +** released. To work around this problem, each unixInodeInfo object
|
| +** maintains a count of the number of pending locks on tha inode.
|
| +** When an attempt is made to close an unixFile, if there are
|
| ** other unixFile open on the same inode that are holding locks, the call
|
| ** to close() the file descriptor is deferred until all of the locks clear.
|
| -** The unixOpenCnt structure keeps a list of file descriptors that need to
|
| +** The unixInodeInfo structure keeps a list of file descriptors that need to
|
| ** be closed and that list is walked (and cleared) when the last lock
|
| ** clears.
|
| **
|
| @@ -666,46 +890,19 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
|
| ** in thread B. But there is no way to know at compile-time which
|
| ** threading library is being used. So there is no way to know at
|
| ** compile-time whether or not thread A can override locks on thread B.
|
| -** We have to do a run-time check to discover the behavior of the
|
| +** One has to do a run-time check to discover the behavior of the
|
| ** current process.
|
| **
|
| -** On systems where thread A is unable to modify locks created by
|
| -** thread B, we have to keep track of which thread created each
|
| -** lock. Hence there is an extra field in the key to the unixLockInfo
|
| -** structure to record this information. And on those systems it
|
| -** is illegal to begin a transaction in one thread and finish it
|
| -** in another. For this latter restriction, there is no work-around.
|
| -** It is a limitation of LinuxThreads.
|
| -*/
|
| -
|
| -/*
|
| -** Set or check the unixFile.tid field. This field is set when an unixFile
|
| -** is first opened. All subsequent uses of the unixFile verify that the
|
| -** same thread is operating on the unixFile. Some operating systems do
|
| -** not allow locks to be overridden by other threads and that restriction
|
| -** means that sqlite3* database handles cannot be moved from one thread
|
| -** to another while locks are held.
|
| -**
|
| -** Version 3.3.1 (2006-01-15): unixFile can be moved from one thread to
|
| -** another as long as we are running on a system that supports threads
|
| -** overriding each others locks (which is now the most common behavior)
|
| -** or if no locks are held. But the unixFile.pLock field needs to be
|
| -** recomputed because its key includes the thread-id. See the
|
| -** transferOwnership() function below for additional information
|
| -*/
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| -# define SET_THREADID(X) (X)->tid = pthread_self()
|
| -# define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \
|
| - !pthread_equal((X)->tid, pthread_self()))
|
| -#else
|
| -# define SET_THREADID(X)
|
| -# define CHECK_THREADID(X) 0
|
| -#endif
|
| +** SQLite used to support LinuxThreads. But support for LinuxThreads
|
| +** was dropped beginning with version 3.7.0. SQLite will still work with
|
| +** LinuxThreads provided that (1) there is no more than one connection
|
| +** per database file in the same process and (2) database connections
|
| +** do not move across threads.
|
| +*/
|
|
|
| /*
|
| ** An instance of the following structure serves as the key used
|
| -** to locate a particular unixOpenCnt structure given its inode. This
|
| -** is the same as the unixLockKey except that the thread ID is omitted.
|
| +** to locate a particular unixInodeInfo object.
|
| */
|
| struct unixFileId {
|
| dev_t dev; /* Device number */
|
| @@ -717,23 +914,6 @@ struct unixFileId {
|
| };
|
|
|
| /*
|
| -** An instance of the following structure serves as the key used
|
| -** to locate a particular unixLockInfo structure given its inode.
|
| -**
|
| -** If threads cannot override each others locks (LinuxThreads), then we
|
| -** set the unixLockKey.tid field to the thread ID. If threads can override
|
| -** each others locks (Posix and NPTL) then tid is always set to zero.
|
| -** tid is omitted if we compile without threading support or on an OS
|
| -** other than linux.
|
| -*/
|
| -struct unixLockKey {
|
| - struct unixFileId fid; /* Unique identifier for the file */
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| - pthread_t tid; /* Thread ID of lock owner. Zero if not using LinuxThreads */
|
| -#endif
|
| -};
|
| -
|
| -/*
|
| ** An instance of the following structure is allocated for each open
|
| ** inode. Or, on LinuxThreads, there is one of these structures for
|
| ** each inode opened by each thread.
|
| @@ -742,227 +922,185 @@ struct unixLockKey {
|
| ** structure contains a pointer to an instance of this object and this
|
| ** object keeps a count of the number of unixFile pointing to it.
|
| */
|
| -struct unixLockInfo {
|
| - struct unixLockKey lockKey; /* The lookup key */
|
| - int cnt; /* Number of SHARED locks held */
|
| - int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
| +struct unixInodeInfo {
|
| + struct unixFileId fileId; /* The lookup key */
|
| + int nShared; /* Number of SHARED locks held */
|
| + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
| + unsigned char bProcessLock; /* An exclusive process lock is held */
|
| int nRef; /* Number of pointers to this structure */
|
| - struct unixLockInfo *pNext; /* List of all unixLockInfo objects */
|
| - struct unixLockInfo *pPrev; /* .... doubly linked */
|
| -};
|
| -
|
| -/*
|
| -** An instance of the following structure is allocated for each open
|
| -** inode. This structure keeps track of the number of locks on that
|
| -** inode. If a close is attempted against an inode that is holding
|
| -** locks, the close is deferred until all locks clear by adding the
|
| -** file descriptor to be closed to the pending list.
|
| -**
|
| -** TODO: Consider changing this so that there is only a single file
|
| -** descriptor for each open file, even when it is opened multiple times.
|
| -** The close() system call would only occur when the last database
|
| -** using the file closes.
|
| -*/
|
| -struct unixOpenCnt {
|
| - struct unixFileId fileId; /* The lookup key */
|
| - int nRef; /* Number of pointers to this structure */
|
| - int nLock; /* Number of outstanding locks */
|
| - UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
| + unixShmNode *pShmNode; /* Shared memory associated with this inode */
|
| + int nLock; /* Number of outstanding file locks */
|
| + UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
| + unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
|
| + unixInodeInfo *pPrev; /* .... doubly linked */
|
| +#if defined(SQLITE_ENABLE_LOCKING_STYLE)
|
| + unsigned long long sharedByte; /* for AFP simulated shared lock */
|
| +#endif
|
| #if OS_VXWORKS
|
| - sem_t *pSem; /* Named POSIX semaphore */
|
| - char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */
|
| + sem_t *pSem; /* Named POSIX semaphore */
|
| + char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */
|
| #endif
|
| - struct unixOpenCnt *pNext, *pPrev; /* List of all unixOpenCnt objects */
|
| };
|
|
|
| /*
|
| -** Lists of all unixLockInfo and unixOpenCnt objects. These used to be hash
|
| -** tables. But the number of objects is rarely more than a dozen and
|
| -** never exceeds a few thousand. And lookup is not on a critical
|
| -** path so a simple linked list will suffice.
|
| +** A lists of all unixInodeInfo objects.
|
| */
|
| -static struct unixLockInfo *lockList = 0;
|
| -static struct unixOpenCnt *openList = 0;
|
| +static unixInodeInfo *inodeList = 0;
|
|
|
| /*
|
| -** This variable remembers whether or not threads can override each others
|
| -** locks.
|
| -**
|
| -** 0: No. Threads cannot override each others locks. (LinuxThreads)
|
| -** 1: Yes. Threads can override each others locks. (Posix & NLPT)
|
| -** -1: We don't know yet.
|
| **
|
| -** On some systems, we know at compile-time if threads can override each
|
| -** others locks. On those systems, the SQLITE_THREAD_OVERRIDE_LOCK macro
|
| -** will be set appropriately. On other systems, we have to check at
|
| -** runtime. On these latter systems, SQLTIE_THREAD_OVERRIDE_LOCK is
|
| -** undefined.
|
| -**
|
| -** This variable normally has file scope only. But during testing, we make
|
| -** it a global so that the test code can change its value in order to verify
|
| -** that the right stuff happens in either case.
|
| -*/
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| -# ifndef SQLITE_THREAD_OVERRIDE_LOCK
|
| -# define SQLITE_THREAD_OVERRIDE_LOCK -1
|
| -# endif
|
| -# ifdef SQLITE_TEST
|
| -int threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK;
|
| -# else
|
| -static int threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK;
|
| -# endif
|
| +** This function - unixLogError_x(), is only ever called via the macro
|
| +** unixLogError().
|
| +**
|
| +** It is invoked after an error occurs in an OS function and errno has been
|
| +** set. It logs a message using sqlite3_log() containing the current value of
|
| +** errno and, if possible, the human-readable equivalent from strerror() or
|
| +** strerror_r().
|
| +**
|
| +** The first argument passed to the macro should be the error code that
|
| +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
|
| +** The two subsequent arguments should be the name of the OS function that
|
| +** failed (e.g. "unlink", "open") and the the associated file-system path,
|
| +** if any.
|
| +*/
|
| +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__)
|
| +static int unixLogErrorAtLine(
|
| + int errcode, /* SQLite error code */
|
| + const char *zFunc, /* Name of OS function that failed */
|
| + const char *zPath, /* File path associated with error */
|
| + int iLine /* Source line number where error occurred */
|
| +){
|
| + char *zErr; /* Message from strerror() or equivalent */
|
| + int iErrno = errno; /* Saved syscall error number */
|
| +
|
| + /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use
|
| + ** the strerror() function to obtain the human-readable error message
|
| + ** equivalent to errno. Otherwise, use strerror_r().
|
| + */
|
| +#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R)
|
| + char aErr[80];
|
| + memset(aErr, 0, sizeof(aErr));
|
| + zErr = aErr;
|
| +
|
| + /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
|
| + ** assume that the system provides the the GNU version of strerror_r() that
|
| + ** returns a pointer to a buffer containing the error message. That pointer
|
| + ** may point to aErr[], or it may point to some static storage somewhere.
|
| + ** Otherwise, assume that the system provides the POSIX version of
|
| + ** strerror_r(), which always writes an error message into aErr[].
|
| + **
|
| + ** If the code incorrectly assumes that it is the POSIX version that is
|
| + ** available, the error message will often be an empty string. Not a
|
| + ** huge problem. Incorrectly concluding that the GNU version is available
|
| + ** could lead to a segfault though.
|
| + */
|
| +#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
|
| + zErr =
|
| +# endif
|
| + strerror_r(iErrno, aErr, sizeof(aErr)-1);
|
| +
|
| +#elif SQLITE_THREADSAFE
|
| + /* This is a threadsafe build, but strerror_r() is not available. */
|
| + zErr = "";
|
| +#else
|
| + /* Non-threadsafe build, use strerror(). */
|
| + zErr = strerror(iErrno);
|
| #endif
|
|
|
| -/*
|
| -** This structure holds information passed into individual test
|
| -** threads by the testThreadLockingBehavior() routine.
|
| -*/
|
| -struct threadTestData {
|
| - int fd; /* File to be locked */
|
| - struct flock lock; /* The locking operation */
|
| - int result; /* Result of the locking operation */
|
| -};
|
| + assert( errcode!=SQLITE_OK );
|
| + if( zPath==0 ) zPath = "";
|
| + sqlite3_log(errcode,
|
| + "os_unix.c:%d: (%d) %s(%s) - %s",
|
| + iLine, iErrno, zFunc, zPath, zErr
|
| + );
|
|
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| -/*
|
| -** This function is used as the main routine for a thread launched by
|
| -** testThreadLockingBehavior(). It tests whether the shared-lock obtained
|
| -** by the main thread in testThreadLockingBehavior() conflicts with a
|
| -** hypothetical write-lock obtained by this thread on the same file.
|
| -**
|
| -** The write-lock is not actually acquired, as this is not possible if
|
| -** the file is open in read-only mode (see ticket #3472).
|
| -*/
|
| -static void *threadLockingTest(void *pArg){
|
| - struct threadTestData *pData = (struct threadTestData*)pArg;
|
| - pData->result = fcntl(pData->fd, F_GETLK, &pData->lock);
|
| - return pArg;
|
| + return errcode;
|
| }
|
| -#endif /* SQLITE_THREADSAFE && defined(__linux__) */
|
|
|
| -
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| /*
|
| -** This procedure attempts to determine whether or not threads
|
| -** can override each others locks then sets the
|
| -** threadsOverrideEachOthersLocks variable appropriately.
|
| +** Close a file descriptor.
|
| +**
|
| +** We assume that close() almost always works, since it is only in a
|
| +** very sick application or on a very sick platform that it might fail.
|
| +** If it does fail, simply leak the file descriptor, but do log the
|
| +** error.
|
| +**
|
| +** Note that it is not safe to retry close() after EINTR since the
|
| +** file descriptor might have already been reused by another thread.
|
| +** So we don't even try to recover from an EINTR. Just log the error
|
| +** and move on.
|
| */
|
| -static void testThreadLockingBehavior(int fd_orig){
|
| - int fd;
|
| - int rc;
|
| - struct threadTestData d;
|
| - struct flock l;
|
| - pthread_t t;
|
| -
|
| - fd = dup(fd_orig);
|
| - if( fd<0 ) return;
|
| - memset(&l, 0, sizeof(l));
|
| - l.l_type = F_RDLCK;
|
| - l.l_len = 1;
|
| - l.l_start = 0;
|
| - l.l_whence = SEEK_SET;
|
| - rc = fcntl(fd_orig, F_SETLK, &l);
|
| - if( rc!=0 ) return;
|
| - memset(&d, 0, sizeof(d));
|
| - d.fd = fd;
|
| - d.lock = l;
|
| - d.lock.l_type = F_WRLCK;
|
| - if( pthread_create(&t, 0, threadLockingTest, &d)==0 ){
|
| - pthread_join(t, 0);
|
| - }
|
| - close(fd);
|
| - if( d.result!=0 ) return;
|
| - threadsOverrideEachOthersLocks = (d.lock.l_type==F_UNLCK);
|
| +static void robust_close(unixFile *pFile, int h, int lineno){
|
| + if( osClose(h) ){
|
| + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close",
|
| + pFile ? pFile->zPath : 0, lineno);
|
| + }
|
| }
|
| -#endif /* SQLITE_THREADSAFE && defined(__linux__) */
|
|
|
| /*
|
| -** Release a unixLockInfo structure previously allocated by findLockInfo().
|
| -**
|
| -** The mutex entered using the unixEnterMutex() function must be held
|
| -** when this function is called.
|
| -*/
|
| -static void releaseLockInfo(struct unixLockInfo *pLock){
|
| - assert( unixMutexHeld() );
|
| - if( pLock ){
|
| - pLock->nRef--;
|
| - if( pLock->nRef==0 ){
|
| - if( pLock->pPrev ){
|
| - assert( pLock->pPrev->pNext==pLock );
|
| - pLock->pPrev->pNext = pLock->pNext;
|
| - }else{
|
| - assert( lockList==pLock );
|
| - lockList = pLock->pNext;
|
| - }
|
| - if( pLock->pNext ){
|
| - assert( pLock->pNext->pPrev==pLock );
|
| - pLock->pNext->pPrev = pLock->pPrev;
|
| - }
|
| - sqlite3_free(pLock);
|
| - }
|
| +** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
|
| +*/
|
| +static void closePendingFds(unixFile *pFile){
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + UnixUnusedFd *p;
|
| + UnixUnusedFd *pNext;
|
| + for(p=pInode->pUnused; p; p=pNext){
|
| + pNext = p->pNext;
|
| + robust_close(pFile, p->fd, __LINE__);
|
| + sqlite3_free(p);
|
| }
|
| + pInode->pUnused = 0;
|
| }
|
|
|
| /*
|
| -** Release a unixOpenCnt structure previously allocated by findLockInfo().
|
| +** Release a unixInodeInfo structure previously allocated by findInodeInfo().
|
| **
|
| ** The mutex entered using the unixEnterMutex() function must be held
|
| ** when this function is called.
|
| */
|
| -static void releaseOpenCnt(struct unixOpenCnt *pOpen){
|
| +static void releaseInodeInfo(unixFile *pFile){
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| assert( unixMutexHeld() );
|
| - if( pOpen ){
|
| - pOpen->nRef--;
|
| - if( pOpen->nRef==0 ){
|
| - if( pOpen->pPrev ){
|
| - assert( pOpen->pPrev->pNext==pOpen );
|
| - pOpen->pPrev->pNext = pOpen->pNext;
|
| + if( ALWAYS(pInode) ){
|
| + pInode->nRef--;
|
| + if( pInode->nRef==0 ){
|
| + assert( pInode->pShmNode==0 );
|
| + closePendingFds(pFile);
|
| + if( pInode->pPrev ){
|
| + assert( pInode->pPrev->pNext==pInode );
|
| + pInode->pPrev->pNext = pInode->pNext;
|
| }else{
|
| - assert( openList==pOpen );
|
| - openList = pOpen->pNext;
|
| + assert( inodeList==pInode );
|
| + inodeList = pInode->pNext;
|
| }
|
| - if( pOpen->pNext ){
|
| - assert( pOpen->pNext->pPrev==pOpen );
|
| - pOpen->pNext->pPrev = pOpen->pPrev;
|
| + if( pInode->pNext ){
|
| + assert( pInode->pNext->pPrev==pInode );
|
| + pInode->pNext->pPrev = pInode->pPrev;
|
| }
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| - assert( !pOpen->pUnused || threadsOverrideEachOthersLocks==0 );
|
| -#endif
|
| -
|
| - /* If pOpen->pUnused is not null, then memory and file-descriptors
|
| - ** are leaked.
|
| - **
|
| - ** This will only happen if, under Linuxthreads, the user has opened
|
| - ** a transaction in one thread, then attempts to close the database
|
| - ** handle from another thread (without first unlocking the db file).
|
| - ** This is a misuse. */
|
| - sqlite3_free(pOpen);
|
| + sqlite3_free(pInode);
|
| }
|
| }
|
| }
|
|
|
| /*
|
| -** Given a file descriptor, locate unixLockInfo and unixOpenCnt structures that
|
| -** describes that file descriptor. Create new ones if necessary. The
|
| -** return values might be uninitialized if an error occurs.
|
| +** Given a file descriptor, locate the unixInodeInfo object that
|
| +** describes that file descriptor. Create a new one if necessary. The
|
| +** return value might be uninitialized if an error occurs.
|
| **
|
| ** The mutex entered using the unixEnterMutex() function must be held
|
| ** when this function is called.
|
| **
|
| ** Return an appropriate error code.
|
| */
|
| -static int findLockInfo(
|
| +static int findInodeInfo(
|
| unixFile *pFile, /* Unix file with file desc used in the key */
|
| - struct unixLockInfo **ppLock, /* Return the unixLockInfo structure here */
|
| - struct unixOpenCnt **ppOpen /* Return the unixOpenCnt structure here */
|
| + unixInodeInfo **ppInode /* Return the unixInodeInfo object here */
|
| ){
|
| int rc; /* System call return code */
|
| int fd; /* The file descriptor for pFile */
|
| - struct unixLockKey lockKey; /* Lookup key for the unixLockInfo structure */
|
| - struct unixFileId fileId; /* Lookup key for the unixOpenCnt struct */
|
| + struct unixFileId fileId; /* Lookup key for the unixInodeInfo */
|
| struct stat statbuf; /* Low-level file information */
|
| - struct unixLockInfo *pLock = 0;/* Candidate unixLockInfo object */
|
| - struct unixOpenCnt *pOpen; /* Candidate unixOpenCnt object */
|
| + unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */
|
|
|
| assert( unixMutexHeld() );
|
|
|
| @@ -970,7 +1108,7 @@ static int findLockInfo(
|
| ** create a unique name for the file.
|
| */
|
| fd = pFile->h;
|
| - rc = fstat(fd, &statbuf);
|
| + rc = osFstat(fd, &statbuf);
|
| if( rc!=0 ){
|
| pFile->lastErrno = errno;
|
| #ifdef EOVERFLOW
|
| @@ -990,12 +1128,13 @@ static int findLockInfo(
|
| ** is a race condition such that another thread has already populated
|
| ** the first page of the database, no damage is done.
|
| */
|
| - if( statbuf.st_size==0 ){
|
| - rc = write(fd, "S", 1);
|
| + if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){
|
| + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
|
| if( rc!=1 ){
|
| + pFile->lastErrno = errno;
|
| return SQLITE_IOERR;
|
| }
|
| - rc = fstat(fd, &statbuf);
|
| + rc = osFstat(fd, &statbuf);
|
| if( rc!=0 ){
|
| pFile->lastErrno = errno;
|
| return SQLITE_IOERR;
|
| @@ -1003,119 +1142,35 @@ static int findLockInfo(
|
| }
|
| #endif
|
|
|
| - memset(&lockKey, 0, sizeof(lockKey));
|
| - lockKey.fid.dev = statbuf.st_dev;
|
| + memset(&fileId, 0, sizeof(fileId));
|
| + fileId.dev = statbuf.st_dev;
|
| #if OS_VXWORKS
|
| - lockKey.fid.pId = pFile->pId;
|
| + fileId.pId = pFile->pId;
|
| #else
|
| - lockKey.fid.ino = statbuf.st_ino;
|
| -#endif
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| - if( threadsOverrideEachOthersLocks<0 ){
|
| - testThreadLockingBehavior(fd);
|
| - }
|
| - lockKey.tid = threadsOverrideEachOthersLocks ? 0 : pthread_self();
|
| + fileId.ino = statbuf.st_ino;
|
| #endif
|
| - fileId = lockKey.fid;
|
| - if( ppLock!=0 ){
|
| - pLock = lockList;
|
| - while( pLock && memcmp(&lockKey, &pLock->lockKey, sizeof(lockKey)) ){
|
| - pLock = pLock->pNext;
|
| - }
|
| - if( pLock==0 ){
|
| - pLock = sqlite3_malloc( sizeof(*pLock) );
|
| - if( pLock==0 ){
|
| - rc = SQLITE_NOMEM;
|
| - goto exit_findlockinfo;
|
| - }
|
| - pLock->lockKey = lockKey;
|
| - pLock->nRef = 1;
|
| - pLock->cnt = 0;
|
| - pLock->locktype = 0;
|
| - pLock->pNext = lockList;
|
| - pLock->pPrev = 0;
|
| - if( lockList ) lockList->pPrev = pLock;
|
| - lockList = pLock;
|
| - }else{
|
| - pLock->nRef++;
|
| + pInode = inodeList;
|
| + while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
|
| + pInode = pInode->pNext;
|
| + }
|
| + if( pInode==0 ){
|
| + pInode = sqlite3_malloc( sizeof(*pInode) );
|
| + if( pInode==0 ){
|
| + return SQLITE_NOMEM;
|
| }
|
| - *ppLock = pLock;
|
| - }
|
| - if( ppOpen!=0 ){
|
| - pOpen = openList;
|
| - while( pOpen && memcmp(&fileId, &pOpen->fileId, sizeof(fileId)) ){
|
| - pOpen = pOpen->pNext;
|
| - }
|
| - if( pOpen==0 ){
|
| - pOpen = sqlite3_malloc( sizeof(*pOpen) );
|
| - if( pOpen==0 ){
|
| - releaseLockInfo(pLock);
|
| - rc = SQLITE_NOMEM;
|
| - goto exit_findlockinfo;
|
| - }
|
| - memset(pOpen, 0, sizeof(*pOpen));
|
| - pOpen->fileId = fileId;
|
| - pOpen->nRef = 1;
|
| - pOpen->pNext = openList;
|
| - if( openList ) openList->pPrev = pOpen;
|
| - openList = pOpen;
|
| - }else{
|
| - pOpen->nRef++;
|
| - }
|
| - *ppOpen = pOpen;
|
| - }
|
| -
|
| -exit_findlockinfo:
|
| - return rc;
|
| -}
|
| -
|
| -/*
|
| -** If we are currently in a different thread than the thread that the
|
| -** unixFile argument belongs to, then transfer ownership of the unixFile
|
| -** over to the current thread.
|
| -**
|
| -** A unixFile is only owned by a thread on systems that use LinuxThreads.
|
| -**
|
| -** Ownership transfer is only allowed if the unixFile is currently unlocked.
|
| -** If the unixFile is locked and an ownership is wrong, then return
|
| -** SQLITE_MISUSE. SQLITE_OK is returned if everything works.
|
| -*/
|
| -#if SQLITE_THREADSAFE && defined(__linux__)
|
| -static int transferOwnership(unixFile *pFile){
|
| - int rc;
|
| - pthread_t hSelf;
|
| - if( threadsOverrideEachOthersLocks ){
|
| - /* Ownership transfers not needed on this system */
|
| - return SQLITE_OK;
|
| - }
|
| - hSelf = pthread_self();
|
| - if( pthread_equal(pFile->tid, hSelf) ){
|
| - /* We are still in the same thread */
|
| - OSTRACE1("No-transfer, same thread\n");
|
| - return SQLITE_OK;
|
| - }
|
| - if( pFile->locktype!=NO_LOCK ){
|
| - /* We cannot change ownership while we are holding a lock! */
|
| - return SQLITE_MISUSE;
|
| - }
|
| - OSTRACE4("Transfer ownership of %d from %d to %d\n",
|
| - pFile->h, pFile->tid, hSelf);
|
| - pFile->tid = hSelf;
|
| - if (pFile->pLock != NULL) {
|
| - releaseLockInfo(pFile->pLock);
|
| - rc = findLockInfo(pFile, &pFile->pLock, 0);
|
| - OSTRACE5("LOCK %d is now %s(%s,%d)\n", pFile->h,
|
| - locktypeName(pFile->locktype),
|
| - locktypeName(pFile->pLock->locktype), pFile->pLock->cnt);
|
| - return rc;
|
| - } else {
|
| - return SQLITE_OK;
|
| + memset(pInode, 0, sizeof(*pInode));
|
| + memcpy(&pInode->fileId, &fileId, sizeof(fileId));
|
| + pInode->nRef = 1;
|
| + pInode->pNext = inodeList;
|
| + pInode->pPrev = 0;
|
| + if( inodeList ) inodeList->pPrev = pInode;
|
| + inodeList = pInode;
|
| + }else{
|
| + pInode->nRef++;
|
| }
|
| + *ppInode = pInode;
|
| + return SQLITE_OK;
|
| }
|
| -#else /* if not SQLITE_THREADSAFE */
|
| - /* On single-threaded builds, ownership transfer is a no-op */
|
| -# define transferOwnership(X) SQLITE_OK
|
| -#endif /* SQLITE_THREADSAFE */
|
|
|
|
|
| /*
|
| @@ -1132,26 +1187,25 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
|
|
| assert( pFile );
|
| - unixEnterMutex(); /* Because pFile->pLock is shared across threads */
|
| + unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
|
|
| /* Check if a thread in this process holds such a lock */
|
| - if( pFile->pLock->locktype>SHARED_LOCK ){
|
| + if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
| reserved = 1;
|
| }
|
|
|
| /* Otherwise see if some other process holds it.
|
| */
|
| #ifndef __DJGPP__
|
| - if( !reserved ){
|
| + if( !reserved && !pFile->pInode->bProcessLock ){
|
| struct flock lock;
|
| lock.l_whence = SEEK_SET;
|
| lock.l_start = RESERVED_BYTE;
|
| lock.l_len = 1;
|
| lock.l_type = F_WRLCK;
|
| - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
|
| - int tErrno = errno;
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
|
| - pFile->lastErrno = tErrno;
|
| + if( osFcntl(pFile->h, F_GETLK, &lock) ){
|
| + rc = SQLITE_IOERR_CHECKRESERVEDLOCK;
|
| + pFile->lastErrno = errno;
|
| } else if( lock.l_type!=F_UNLCK ){
|
| reserved = 1;
|
| }
|
| @@ -1159,70 +1213,61 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| #endif
|
|
|
| unixLeaveMutex();
|
| - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved));
|
|
|
| *pResOut = reserved;
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Perform a file locking operation on a range of bytes in a file.
|
| -** The "op" parameter should be one of F_RDLCK, F_WRLCK, or F_UNLCK.
|
| -** Return 0 on success or -1 for failure. On failure, write the error
|
| -** code into *pErrcode.
|
| -**
|
| -** If the SQLITE_WHOLE_FILE_LOCKING bit is clear, then only lock
|
| -** the range of bytes on the locking page between SHARED_FIRST and
|
| -** SHARED_SIZE. If SQLITE_WHOLE_FILE_LOCKING is set, then lock all
|
| -** bytes from 0 up to but not including PENDING_BYTE, and all bytes
|
| -** that follow SHARED_FIRST.
|
| -**
|
| -** In other words, of SQLITE_WHOLE_FILE_LOCKING if false (the historical
|
| -** default case) then only lock a small range of bytes from SHARED_FIRST
|
| -** through SHARED_FIRST+SHARED_SIZE-1. But if SQLITE_WHOLE_FILE_LOCKING is
|
| -** true then lock every byte in the file except for PENDING_BYTE and
|
| -** RESERVED_BYTE.
|
| -**
|
| -** SQLITE_WHOLE_FILE_LOCKING=true overlaps SQLITE_WHOLE_FILE_LOCKING=false
|
| -** and so the locking schemes are compatible. One type of lock will
|
| -** effectively exclude the other type. The reason for using the
|
| -** SQLITE_WHOLE_FILE_LOCKING=true is that by indicating the full range
|
| -** of bytes to be read or written, we give hints to NFS to help it
|
| -** maintain cache coherency. On the other hand, whole file locking
|
| -** is slower, so we don't want to use it except for NFS.
|
| -*/
|
| -static int rangeLock(unixFile *pFile, int op, int *pErrcode){
|
| - struct flock lock;
|
| +** Attempt to set a system-lock on the file pFile. The lock is
|
| +** described by pLock.
|
| +**
|
| +** If the pFile was opened read/write from unix-excl, then the only lock
|
| +** ever obtained is an exclusive lock, and it is obtained exactly once
|
| +** the first time any lock is attempted. All subsequent system locking
|
| +** operations become no-ops. Locking operations still happen internally,
|
| +** in order to coordinate access between separate database connections
|
| +** within this process, but all of that is handled in memory and the
|
| +** operating system does not participate.
|
| +**
|
| +** This function is a pass-through to fcntl(F_SETLK) if pFile is using
|
| +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl"
|
| +** and is read-only.
|
| +**
|
| +** Zero is returned if the call completes successfully, or -1 if a call
|
| +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()).
|
| +*/
|
| +static int unixFileLock(unixFile *pFile, struct flock *pLock){
|
| int rc;
|
| - lock.l_type = op;
|
| - lock.l_start = SHARED_FIRST;
|
| - lock.l_whence = SEEK_SET;
|
| - if( (pFile->fileFlags & SQLITE_WHOLE_FILE_LOCKING)==0 ){
|
| - lock.l_len = SHARED_SIZE;
|
| - rc = fcntl(pFile->h, F_SETLK, &lock);
|
| - *pErrcode = errno;
|
| - }else{
|
| - lock.l_len = 0;
|
| - rc = fcntl(pFile->h, F_SETLK, &lock);
|
| - *pErrcode = errno;
|
| - if( NEVER(op==F_UNLCK) || rc!=(-1) ){
|
| - lock.l_start = 0;
|
| - lock.l_len = PENDING_BYTE;
|
| - rc = fcntl(pFile->h, F_SETLK, &lock);
|
| - if( ALWAYS(op!=F_UNLCK) && rc==(-1) ){
|
| - *pErrcode = errno;
|
| - lock.l_type = F_UNLCK;
|
| - lock.l_start = SHARED_FIRST;
|
| - lock.l_len = 0;
|
| - fcntl(pFile->h, F_SETLK, &lock);
|
| - }
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + assert( unixMutexHeld() );
|
| + assert( pInode!=0 );
|
| + if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock)
|
| + && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0)
|
| + ){
|
| + if( pInode->bProcessLock==0 ){
|
| + struct flock lock;
|
| + assert( pInode->nLock==0 );
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + lock.l_type = F_WRLCK;
|
| + rc = osFcntl(pFile->h, F_SETLK, &lock);
|
| + if( rc<0 ) return rc;
|
| + pInode->bProcessLock = 1;
|
| + pInode->nLock++;
|
| + }else{
|
| + rc = 0;
|
| }
|
| + }else{
|
| + rc = osFcntl(pFile->h, F_SETLK, pLock);
|
| }
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -1245,7 +1290,7 @@ static int rangeLock(unixFile *pFile, int op, int *pErrcode){
|
| ** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| ** routine to lower a locking level.
|
| */
|
| -static int unixLock(sqlite3_file *id, int locktype){
|
| +static int unixLock(sqlite3_file *id, int eFileLock){
|
| /* The following describes the implementation of the various locks and
|
| ** lock transitions in terms of the POSIX advisory shared and exclusive
|
| ** lock primitives (called read-locks and write-locks below, to avoid
|
| @@ -1286,23 +1331,22 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| */
|
| int rc = SQLITE_OK;
|
| unixFile *pFile = (unixFile*)id;
|
| - struct unixLockInfo *pLock = pFile->pLock;
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| struct flock lock;
|
| - int s = 0;
|
| - int tErrno;
|
| + int tErrno = 0;
|
|
|
| assert( pFile );
|
| - OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", pFile->h,
|
| - locktypeName(locktype), locktypeName(pFile->locktype),
|
| - locktypeName(pLock->locktype), pLock->cnt , getpid());
|
| + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
|
| + azFileLock(eFileLock), azFileLock(pFile->eFileLock),
|
| + azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
|
|
|
| /* If there is already a lock of this type or more restrictive on the
|
| ** unixFile, do nothing. Don't use the end_lock: exit path, as
|
| ** unixEnterMutex() hasn't been called yet.
|
| */
|
| - if( pFile->locktype>=locktype ){
|
| - OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h,
|
| - locktypeName(locktype));
|
| + if( pFile->eFileLock>=eFileLock ){
|
| + OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h,
|
| + azFileLock(eFileLock)));
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -1311,28 +1355,20 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| ** (2) SQLite never explicitly requests a pendig lock.
|
| ** (3) A shared lock is always held when a reserve lock is requested.
|
| */
|
| - assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
|
| - assert( locktype!=PENDING_LOCK );
|
| - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
|
| + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
|
| + assert( eFileLock!=PENDING_LOCK );
|
| + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
|
|
|
| - /* This mutex is needed because pFile->pLock is shared across threads
|
| + /* This mutex is needed because pFile->pInode is shared across threads
|
| */
|
| unixEnterMutex();
|
| -
|
| - /* Make sure the current thread owns the pFile.
|
| - */
|
| - rc = transferOwnership(pFile);
|
| - if( rc!=SQLITE_OK ){
|
| - unixLeaveMutex();
|
| - return rc;
|
| - }
|
| - pLock = pFile->pLock;
|
| + pInode = pFile->pInode;
|
|
|
| /* If some thread using this PID has a lock via a different unixFile*
|
| ** handle that precludes the requested lock, return BUSY.
|
| */
|
| - if( (pFile->locktype!=pLock->locktype &&
|
| - (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
|
| + if( (pFile->eFileLock!=pInode->eFileLock &&
|
| + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
|
| ){
|
| rc = SQLITE_BUSY;
|
| goto end_lock;
|
| @@ -1342,14 +1378,14 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| ** has a SHARED or RESERVED lock, then increment reference counts and
|
| ** return SQLITE_OK.
|
| */
|
| - if( locktype==SHARED_LOCK &&
|
| - (pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){
|
| - assert( locktype==SHARED_LOCK );
|
| - assert( pFile->locktype==0 );
|
| - assert( pLock->cnt>0 );
|
| - pFile->locktype = SHARED_LOCK;
|
| - pLock->cnt++;
|
| - pFile->pOpen->nLock++;
|
| + if( eFileLock==SHARED_LOCK &&
|
| + (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
|
| + assert( eFileLock==SHARED_LOCK );
|
| + assert( pFile->eFileLock==0 );
|
| + assert( pInode->nShared>0 );
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nShared++;
|
| + pInode->nLock++;
|
| goto end_lock;
|
| }
|
|
|
| @@ -1360,16 +1396,15 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| */
|
| lock.l_len = 1L;
|
| lock.l_whence = SEEK_SET;
|
| - if( locktype==SHARED_LOCK
|
| - || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK)
|
| + if( eFileLock==SHARED_LOCK
|
| + || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
|
| ){
|
| - lock.l_type = (locktype==SHARED_LOCK?F_RDLCK:F_WRLCK);
|
| + lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
|
| lock.l_start = PENDING_BYTE;
|
| - s = fcntl(pFile->h, F_SETLK, &lock);
|
| - if( s==(-1) ){
|
| + if( unixFileLock(pFile, &lock) ){
|
| tErrno = errno;
|
| rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| + if( rc!=SQLITE_BUSY ){
|
| pFile->lastErrno = tErrno;
|
| }
|
| goto end_lock;
|
| @@ -1380,39 +1415,40 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| /* If control gets to this point, then actually go ahead and make
|
| ** operating system calls for the specified lock.
|
| */
|
| - if( locktype==SHARED_LOCK ){
|
| - assert( pLock->cnt==0 );
|
| - assert( pLock->locktype==0 );
|
| + if( eFileLock==SHARED_LOCK ){
|
| + assert( pInode->nShared==0 );
|
| + assert( pInode->eFileLock==0 );
|
| + assert( rc==SQLITE_OK );
|
|
|
| /* Now get the read-lock */
|
| - s = rangeLock(pFile, F_RDLCK, &tErrno);
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + if( unixFileLock(pFile, &lock) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + }
|
|
|
| /* Drop the temporary PENDING lock */
|
| lock.l_start = PENDING_BYTE;
|
| lock.l_len = 1L;
|
| lock.l_type = F_UNLCK;
|
| - if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
|
| - if( s != -1 ){
|
| - /* This could happen with a network mount */
|
| - tErrno = errno;
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| - pFile->lastErrno = tErrno;
|
| - }
|
| - goto end_lock;
|
| - }
|
| + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){
|
| + /* This could happen with a network mount */
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| }
|
| - if( s==(-1) ){
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| +
|
| + if( rc ){
|
| + if( rc!=SQLITE_BUSY ){
|
| pFile->lastErrno = tErrno;
|
| }
|
| + goto end_lock;
|
| }else{
|
| - pFile->locktype = SHARED_LOCK;
|
| - pFile->pOpen->nLock++;
|
| - pLock->cnt = 1;
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nLock++;
|
| + pInode->nShared = 1;
|
| }
|
| - }else if( locktype==EXCLUSIVE_LOCK && pLock->cnt>1 ){
|
| + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
|
| /* We are trying for an exclusive lock but another thread in this
|
| ** same process is still holding a shared lock. */
|
| rc = SQLITE_BUSY;
|
| @@ -1421,23 +1457,22 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| ** assumed that there is a SHARED or greater lock on the file
|
| ** already.
|
| */
|
| - assert( 0!=pFile->locktype );
|
| + assert( 0!=pFile->eFileLock );
|
| lock.l_type = F_WRLCK;
|
| - switch( locktype ){
|
| - case RESERVED_LOCK:
|
| - lock.l_start = RESERVED_BYTE;
|
| - s = fcntl(pFile->h, F_SETLK, &lock);
|
| - tErrno = errno;
|
| - break;
|
| - case EXCLUSIVE_LOCK:
|
| - s = rangeLock(pFile, F_WRLCK, &tErrno);
|
| - break;
|
| - default:
|
| - assert(0);
|
| +
|
| + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK );
|
| + if( eFileLock==RESERVED_LOCK ){
|
| + lock.l_start = RESERVED_BYTE;
|
| + lock.l_len = 1L;
|
| + }else{
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| }
|
| - if( s==(-1) ){
|
| +
|
| + if( unixFileLock(pFile, &lock) ){
|
| + tErrno = errno;
|
| rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| + if( rc!=SQLITE_BUSY ){
|
| pFile->lastErrno = tErrno;
|
| }
|
| }
|
| @@ -1451,8 +1486,8 @@ static int unixLock(sqlite3_file *id, int locktype){
|
| ** write operation (not a hot journal rollback).
|
| */
|
| if( rc==SQLITE_OK
|
| - && pFile->locktype<=SHARED_LOCK
|
| - && locktype==RESERVED_LOCK
|
| + && pFile->eFileLock<=SHARED_LOCK
|
| + && eFileLock==RESERVED_LOCK
|
| ){
|
| pFile->transCntrChng = 0;
|
| pFile->dbUpdate = 0;
|
| @@ -1462,47 +1497,17 @@ static int unixLock(sqlite3_file *id, int locktype){
|
|
|
|
|
| if( rc==SQLITE_OK ){
|
| - pFile->locktype = locktype;
|
| - pLock->locktype = locktype;
|
| - }else if( locktype==EXCLUSIVE_LOCK ){
|
| - pFile->locktype = PENDING_LOCK;
|
| - pLock->locktype = PENDING_LOCK;
|
| + pFile->eFileLock = eFileLock;
|
| + pInode->eFileLock = eFileLock;
|
| + }else if( eFileLock==EXCLUSIVE_LOCK ){
|
| + pFile->eFileLock = PENDING_LOCK;
|
| + pInode->eFileLock = PENDING_LOCK;
|
| }
|
|
|
| end_lock:
|
| unixLeaveMutex();
|
| - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
|
| - rc==SQLITE_OK ? "ok" : "failed");
|
| - return rc;
|
| -}
|
| -
|
| -/*
|
| -** Close all file descriptors accumuated in the unixOpenCnt->pUnused list.
|
| -** If all such file descriptors are closed without error, the list is
|
| -** cleared and SQLITE_OK returned.
|
| -**
|
| -** Otherwise, if an error occurs, then successfully closed file descriptor
|
| -** entries are removed from the list, and SQLITE_IOERR_CLOSE returned.
|
| -** not deleted and SQLITE_IOERR_CLOSE returned.
|
| -*/
|
| -static int closePendingFds(unixFile *pFile){
|
| - int rc = SQLITE_OK;
|
| - struct unixOpenCnt *pOpen = pFile->pOpen;
|
| - UnixUnusedFd *pError = 0;
|
| - UnixUnusedFd *p;
|
| - UnixUnusedFd *pNext;
|
| - for(p=pOpen->pUnused; p; p=pNext){
|
| - pNext = p->pNext;
|
| - if( close(p->fd) ){
|
| - pFile->lastErrno = errno;
|
| - rc = SQLITE_IOERR_CLOSE;
|
| - p->pNext = pError;
|
| - pError = p;
|
| - }else{
|
| - sqlite3_free(p);
|
| - }
|
| - }
|
| - pOpen->pUnused = pError;
|
| + OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| return rc;
|
| }
|
|
|
| @@ -1511,46 +1516,49 @@ static int closePendingFds(unixFile *pFile){
|
| ** pUnused list.
|
| */
|
| static void setPendingFd(unixFile *pFile){
|
| - struct unixOpenCnt *pOpen = pFile->pOpen;
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| UnixUnusedFd *p = pFile->pUnused;
|
| - p->pNext = pOpen->pUnused;
|
| - pOpen->pUnused = p;
|
| + p->pNext = pInode->pUnused;
|
| + pInode->pUnused = p;
|
| pFile->h = -1;
|
| pFile->pUnused = 0;
|
| }
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| ** the requested locking level, this routine is a no-op.
|
| +**
|
| +** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
|
| +** the byte range is divided into 2 parts and the first part is unlocked then
|
| +** set to a read lock, then the other part is simply unlocked. This works
|
| +** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
|
| +** remove the write lock on a region when a read lock is set.
|
| */
|
| -static int unixUnlock(sqlite3_file *id, int locktype){
|
| - unixFile *pFile = (unixFile*)id; /* The open file */
|
| - struct unixLockInfo *pLock; /* Structure describing current lock state */
|
| - struct flock lock; /* Information passed into fcntl() */
|
| - int rc = SQLITE_OK; /* Return code from this interface */
|
| - int h; /* The underlying file descriptor */
|
| - int tErrno; /* Error code from system call errors */
|
| +static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
| + unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode;
|
| + struct flock lock;
|
| + int rc = SQLITE_OK;
|
| + int h;
|
|
|
| assert( pFile );
|
| - OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype,
|
| - pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid());
|
| + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
|
| + getpid()));
|
|
|
| - assert( locktype<=SHARED_LOCK );
|
| - if( pFile->locktype<=locktype ){
|
| + assert( eFileLock<=SHARED_LOCK );
|
| + if( pFile->eFileLock<=eFileLock ){
|
| return SQLITE_OK;
|
| }
|
| - if( CHECK_THREADID(pFile) ){
|
| - return SQLITE_MISUSE;
|
| - }
|
| unixEnterMutex();
|
| h = pFile->h;
|
| - pLock = pFile->pLock;
|
| - assert( pLock->cnt!=0 );
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| - assert( pLock->locktype==pFile->locktype );
|
| + pInode = pFile->pInode;
|
| + assert( pInode->nShared!=0 );
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + assert( pInode->eFileLock==pFile->eFileLock );
|
| SimulateIOErrorBenign(1);
|
| SimulateIOError( h=(-1) )
|
| SimulateIOErrorBenign(0);
|
| @@ -1564,62 +1572,122 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
| ** the file has changed and hence might not know to flush their
|
| ** cache. The use of a stale cache can lead to database corruption.
|
| */
|
| +#if 0
|
| assert( pFile->inNormalWrite==0
|
| || pFile->dbUpdate==0
|
| || pFile->transCntrChng==1 );
|
| +#endif
|
| pFile->inNormalWrite = 0;
|
| #endif
|
|
|
| + /* downgrading to a shared lock on NFS involves clearing the write lock
|
| + ** before establishing the readlock - to avoid a race condition we downgrade
|
| + ** the lock in 2 blocks, so that part of the range will be covered by a
|
| + ** write lock until the rest is covered by a read lock:
|
| + ** 1: [WWWWW]
|
| + ** 2: [....W]
|
| + ** 3: [RRRRW]
|
| + ** 4: [RRRR.]
|
| + */
|
| + if( eFileLock==SHARED_LOCK ){
|
|
|
| - if( locktype==SHARED_LOCK ){
|
| - if( rangeLock(pFile, F_RDLCK, &tErrno)==(-1) ){
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| - pFile->lastErrno = tErrno;
|
| - }
|
| - goto end_unlock;
|
| - }
|
| +#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE
|
| + (void)handleNFSUnlock;
|
| + assert( handleNFSUnlock==0 );
|
| +#endif
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + if( handleNFSUnlock ){
|
| + int tErrno; /* Error code from system call errors */
|
| + off_t divSize = SHARED_SIZE - 1;
|
| +
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + pFile->lastErrno = tErrno;
|
| + }
|
| + goto end_unlock;
|
| + }
|
| + lock.l_type = F_RDLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + pFile->lastErrno = tErrno;
|
| + }
|
| + goto end_unlock;
|
| + }
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST+divSize;
|
| + lock.l_len = SHARED_SIZE-divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + pFile->lastErrno = tErrno;
|
| + }
|
| + goto end_unlock;
|
| + }
|
| + }else
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| + {
|
| + lock.l_type = F_RDLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + if( unixFileLock(pFile, &lock) ){
|
| + /* In theory, the call to unixFileLock() cannot fail because another
|
| + ** process is holding an incompatible lock. If it does, this
|
| + ** indicates that the other process is not following the locking
|
| + ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning
|
| + ** SQLITE_BUSY would confuse the upper layer (in practice it causes
|
| + ** an assert to fail). */
|
| + rc = SQLITE_IOERR_RDLOCK;
|
| + pFile->lastErrno = errno;
|
| + goto end_unlock;
|
| + }
|
| + }
|
| }
|
| lock.l_type = F_UNLCK;
|
| lock.l_whence = SEEK_SET;
|
| lock.l_start = PENDING_BYTE;
|
| lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
|
| - if( fcntl(h, F_SETLK, &lock)!=(-1) ){
|
| - pLock->locktype = SHARED_LOCK;
|
| + if( unixFileLock(pFile, &lock)==0 ){
|
| + pInode->eFileLock = SHARED_LOCK;
|
| }else{
|
| - tErrno = errno;
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| - pFile->lastErrno = tErrno;
|
| - }
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + pFile->lastErrno = errno;
|
| goto end_unlock;
|
| }
|
| }
|
| - if( locktype==NO_LOCK ){
|
| - struct unixOpenCnt *pOpen;
|
| -
|
| + if( eFileLock==NO_LOCK ){
|
| /* Decrement the shared lock counter. Release the lock using an
|
| ** OS call only when all threads in this same process have released
|
| ** the lock.
|
| */
|
| - pLock->cnt--;
|
| - if( pLock->cnt==0 ){
|
| + pInode->nShared--;
|
| + if( pInode->nShared==0 ){
|
| lock.l_type = F_UNLCK;
|
| lock.l_whence = SEEK_SET;
|
| lock.l_start = lock.l_len = 0L;
|
| SimulateIOErrorBenign(1);
|
| SimulateIOError( h=(-1) )
|
| SimulateIOErrorBenign(0);
|
| - if( fcntl(h, F_SETLK, &lock)!=(-1) ){
|
| - pLock->locktype = NO_LOCK;
|
| + if( unixFileLock(pFile, &lock)==0 ){
|
| + pInode->eFileLock = NO_LOCK;
|
| }else{
|
| - tErrno = errno;
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| - if( IS_LOCK_ERROR(rc) ){
|
| - pFile->lastErrno = tErrno;
|
| - }
|
| - pLock->locktype = NO_LOCK;
|
| - pFile->locktype = NO_LOCK;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + pFile->lastErrno = errno;
|
| + pInode->eFileLock = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| }
|
| }
|
|
|
| @@ -1627,24 +1695,31 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
| ** count reaches zero, close any other file descriptors whose close
|
| ** was deferred because of outstanding locks.
|
| */
|
| - pOpen = pFile->pOpen;
|
| - pOpen->nLock--;
|
| - assert( pOpen->nLock>=0 );
|
| - if( pOpen->nLock==0 ){
|
| - int rc2 = closePendingFds(pFile);
|
| - if( rc==SQLITE_OK ){
|
| - rc = rc2;
|
| - }
|
| + pInode->nLock--;
|
| + assert( pInode->nLock>=0 );
|
| + if( pInode->nLock==0 ){
|
| + closePendingFds(pFile);
|
| }
|
| }
|
|
|
| end_unlock:
|
| unixLeaveMutex();
|
| - if( rc==SQLITE_OK ) pFile->locktype = locktype;
|
| + if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
| return rc;
|
| }
|
|
|
| /*
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| +** must be either NO_LOCK or SHARED_LOCK.
|
| +**
|
| +** If the locking level of the file descriptor is already at or below
|
| +** the requested locking level, this routine is a no-op.
|
| +*/
|
| +static int unixUnlock(sqlite3_file *id, int eFileLock){
|
| + return posixUnlock(id, eFileLock, 0);
|
| +}
|
| +
|
| +/*
|
| ** This function performs the parts of the "close file" operation
|
| ** common to all locking schemes. It closes the directory and file
|
| ** handles, if they are valid, and sets all fields of the unixFile
|
| @@ -1656,37 +1731,27 @@ end_unlock:
|
| */
|
| static int closeUnixFile(sqlite3_file *id){
|
| unixFile *pFile = (unixFile*)id;
|
| - if( pFile ){
|
| - if( pFile->dirfd>=0 ){
|
| - int err = close(pFile->dirfd);
|
| - if( err ){
|
| - pFile->lastErrno = errno;
|
| - return SQLITE_IOERR_DIR_CLOSE;
|
| - }else{
|
| - pFile->dirfd=-1;
|
| - }
|
| - }
|
| - if( pFile->h>=0 ){
|
| - int err = close(pFile->h);
|
| - if( err ){
|
| - pFile->lastErrno = errno;
|
| - return SQLITE_IOERR_CLOSE;
|
| - }
|
| - }
|
| + if( pFile->dirfd>=0 ){
|
| + robust_close(pFile, pFile->dirfd, __LINE__);
|
| + pFile->dirfd=-1;
|
| + }
|
| + if( pFile->h>=0 ){
|
| + robust_close(pFile, pFile->h, __LINE__);
|
| + pFile->h = -1;
|
| + }
|
| #if OS_VXWORKS
|
| - if( pFile->pId ){
|
| - if( pFile->isDelete ){
|
| - unlink(pFile->pId->zCanonicalName);
|
| - }
|
| - vxworksReleaseFileId(pFile->pId);
|
| - pFile->pId = 0;
|
| + if( pFile->pId ){
|
| + if( pFile->isDelete ){
|
| + unlink(pFile->pId->zCanonicalName);
|
| }
|
| -#endif
|
| - OSTRACE2("CLOSE %-3d\n", pFile->h);
|
| - OpenCounter(-1);
|
| - sqlite3_free(pFile->pUnused);
|
| - memset(pFile, 0, sizeof(unixFile));
|
| + vxworksReleaseFileId(pFile->pId);
|
| + pFile->pId = 0;
|
| }
|
| +#endif
|
| + OSTRACE(("CLOSE %-3d\n", pFile->h));
|
| + OpenCounter(-1);
|
| + sqlite3_free(pFile->pUnused);
|
| + memset(pFile, 0, sizeof(unixFile));
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -1695,23 +1760,25 @@ static int closeUnixFile(sqlite3_file *id){
|
| */
|
| static int unixClose(sqlite3_file *id){
|
| int rc = SQLITE_OK;
|
| - if( id ){
|
| - unixFile *pFile = (unixFile *)id;
|
| - unixUnlock(id, NO_LOCK);
|
| - unixEnterMutex();
|
| - if( pFile->pOpen && pFile->pOpen->nLock ){
|
| - /* If there are outstanding locks, do not actually close the file just
|
| - ** yet because that would clear those locks. Instead, add the file
|
| - ** descriptor to pOpen->pUnused list. It will be automatically closed
|
| - ** when the last lock is cleared.
|
| - */
|
| - setPendingFd(pFile);
|
| - }
|
| - releaseLockInfo(pFile->pLock);
|
| - releaseOpenCnt(pFile->pOpen);
|
| - rc = closeUnixFile(id);
|
| - unixLeaveMutex();
|
| + unixFile *pFile = (unixFile *)id;
|
| + unixUnlock(id, NO_LOCK);
|
| + unixEnterMutex();
|
| +
|
| + /* unixFile.pInode is always valid here. Otherwise, a different close
|
| + ** routine (e.g. nolockClose()) would be called instead.
|
| + */
|
| + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
|
| + if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
|
| + /* If there are outstanding locks, do not actually close the file just
|
| + ** yet because that would clear those locks. Instead, add the file
|
| + ** descriptor to pInode->pUnused list. It will be automatically closed
|
| + ** when the last lock is cleared.
|
| + */
|
| + setPendingFd(pFile);
|
| }
|
| + releaseInodeInfo(pFile);
|
| + rc = closeUnixFile(id);
|
| + unixLeaveMutex();
|
| return rc;
|
| }
|
|
|
| @@ -1807,22 +1874,22 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| assert( pFile );
|
|
|
| /* Check if a thread in this process holds such a lock */
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| /* Either this connection or some other connection in the same process
|
| ** holds a lock on the file. No need to check further. */
|
| reserved = 1;
|
| }else{
|
| /* The lock is held if and only if the lockfile exists */
|
| const char *zLockFile = (const char*)pFile->lockingContext;
|
| - reserved = access(zLockFile, 0)==0;
|
| + reserved = osAccess(zLockFile, 0)==0;
|
| }
|
| - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
|
| *pResOut = reserved;
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -1848,7 +1915,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| ** With dotfile locking, we really only support state (4): EXCLUSIVE.
|
| ** But we track the other locking levels internally.
|
| */
|
| -static int dotlockLock(sqlite3_file *id, int locktype) {
|
| +static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| int fd;
|
| char *zLockFile = (char *)pFile->lockingContext;
|
| @@ -1858,8 +1925,8 @@ static int dotlockLock(sqlite3_file *id, int locktype) {
|
| /* If we have any lock, then the lock file already exists. All we have
|
| ** to do is adjust our internal record of the lock level.
|
| */
|
| - if( pFile->locktype > NO_LOCK ){
|
| - pFile->locktype = locktype;
|
| + if( pFile->eFileLock > NO_LOCK ){
|
| + pFile->eFileLock = eFileLock;
|
| #if !OS_VXWORKS
|
| /* Always update the timestamp on the old file */
|
| utimes(zLockFile, NULL);
|
| @@ -1868,7 +1935,7 @@ static int dotlockLock(sqlite3_file *id, int locktype) {
|
| }
|
|
|
| /* grab an exclusive lock */
|
| - fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
|
| + fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
|
| if( fd<0 ){
|
| /* failed to open/create the file, someone else may have stolen the lock */
|
| int tErrno = errno;
|
| @@ -1882,18 +1949,15 @@ static int dotlockLock(sqlite3_file *id, int locktype) {
|
| }
|
| return rc;
|
| }
|
| - if( close(fd) ){
|
| - pFile->lastErrno = errno;
|
| - rc = SQLITE_IOERR_CLOSE;
|
| - }
|
| + robust_close(pFile, fd, __LINE__);
|
|
|
| /* got it, set the type and return ok */
|
| - pFile->locktype = locktype;
|
| + pFile->eFileLock = eFileLock;
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| @@ -1901,42 +1965,42 @@ static int dotlockLock(sqlite3_file *id, int locktype) {
|
| **
|
| ** When the locking level reaches NO_LOCK, delete the lock file.
|
| */
|
| -static int dotlockUnlock(sqlite3_file *id, int locktype) {
|
| +static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| char *zLockFile = (char *)pFile->lockingContext;
|
|
|
| assert( pFile );
|
| - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
|
| - pFile->locktype, getpid());
|
| - assert( locktype<=SHARED_LOCK );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, getpid()));
|
| + assert( eFileLock<=SHARED_LOCK );
|
|
|
| /* no-op if possible */
|
| - if( pFile->locktype==locktype ){
|
| + if( pFile->eFileLock==eFileLock ){
|
| return SQLITE_OK;
|
| }
|
|
|
| /* To downgrade to shared, simply update our internal notion of the
|
| ** lock state. No need to mess with the file on disk.
|
| */
|
| - if( locktype==SHARED_LOCK ){
|
| - pFile->locktype = SHARED_LOCK;
|
| + if( eFileLock==SHARED_LOCK ){
|
| + pFile->eFileLock = SHARED_LOCK;
|
| return SQLITE_OK;
|
| }
|
|
|
| /* To fully unlock the database, delete the lock file */
|
| - assert( locktype==NO_LOCK );
|
| + assert( eFileLock==NO_LOCK );
|
| if( unlink(zLockFile) ){
|
| int rc = 0;
|
| int tErrno = errno;
|
| if( ENOENT != tErrno ){
|
| - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| }
|
| if( IS_LOCK_ERROR(rc) ){
|
| pFile->lastErrno = tErrno;
|
| }
|
| return rc;
|
| }
|
| - pFile->locktype = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -1974,6 +2038,20 @@ static int dotlockClose(sqlite3_file *id) {
|
| #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
|
|
| /*
|
| +** Retry flock() calls that fail with EINTR
|
| +*/
|
| +#ifdef EINTR
|
| +static int robust_flock(int fd, int op){
|
| + int rc;
|
| + do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR );
|
| + return rc;
|
| +}
|
| +#else
|
| +# define robust_flock(a,b) flock(a,b)
|
| +#endif
|
| +
|
| +
|
| +/*
|
| ** This routine checks if there is a RESERVED lock held on the specified
|
| ** file by this or any other process. If such a lock is held, set *pResOut
|
| ** to a non-zero value otherwise *pResOut is set to zero. The return value
|
| @@ -1989,21 +2067,21 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| assert( pFile );
|
|
|
| /* Check if a thread in this process holds such a lock */
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| reserved = 1;
|
| }
|
|
|
| /* Otherwise see if some other process holds it. */
|
| if( !reserved ){
|
| /* attempt to get the lock */
|
| - int lrc = flock(pFile->h, LOCK_EX | LOCK_NB);
|
| + int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB);
|
| if( !lrc ){
|
| /* got the lock, unlock it */
|
| - lrc = flock(pFile->h, LOCK_UN);
|
| + lrc = robust_flock(pFile->h, LOCK_UN);
|
| if ( lrc ) {
|
| int tErrno = errno;
|
| /* unlock failed with an error */
|
| - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| + lrc = SQLITE_IOERR_UNLOCK;
|
| if( IS_LOCK_ERROR(lrc) ){
|
| pFile->lastErrno = tErrno;
|
| rc = lrc;
|
| @@ -2020,7 +2098,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| }
|
| }
|
| }
|
| - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved));
|
|
|
| #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
| @@ -2033,7 +2111,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -2061,7 +2139,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| ** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| ** routine to lower a locking level.
|
| */
|
| -static int flockLock(sqlite3_file *id, int locktype) {
|
| +static int flockLock(sqlite3_file *id, int eFileLock) {
|
| int rc = SQLITE_OK;
|
| unixFile *pFile = (unixFile*)id;
|
|
|
| @@ -2069,14 +2147,14 @@ static int flockLock(sqlite3_file *id, int locktype) {
|
|
|
| /* if we already have a lock, it is exclusive.
|
| ** Just adjust level and punt on outta here. */
|
| - if (pFile->locktype > NO_LOCK) {
|
| - pFile->locktype = locktype;
|
| + if (pFile->eFileLock > NO_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| return SQLITE_OK;
|
| }
|
|
|
| /* grab an exclusive lock */
|
|
|
| - if (flock(pFile->h, LOCK_EX | LOCK_NB)) {
|
| + if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) {
|
| int tErrno = errno;
|
| /* didn't get, must be busy */
|
| rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| @@ -2085,10 +2163,10 @@ static int flockLock(sqlite3_file *id, int locktype) {
|
| }
|
| } else {
|
| /* got it, set the type and return ok */
|
| - pFile->locktype = locktype;
|
| + pFile->eFileLock = eFileLock;
|
| }
|
| - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
|
| - rc==SQLITE_OK ? "ok" : "failed");
|
| + OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
| rc = SQLITE_BUSY;
|
| @@ -2099,48 +2177,39 @@ static int flockLock(sqlite3_file *id, int locktype) {
|
|
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| ** the requested locking level, this routine is a no-op.
|
| */
|
| -static int flockUnlock(sqlite3_file *id, int locktype) {
|
| +static int flockUnlock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
|
|
| assert( pFile );
|
| - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
|
| - pFile->locktype, getpid());
|
| - assert( locktype<=SHARED_LOCK );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, getpid()));
|
| + assert( eFileLock<=SHARED_LOCK );
|
|
|
| /* no-op if possible */
|
| - if( pFile->locktype==locktype ){
|
| + if( pFile->eFileLock==eFileLock ){
|
| return SQLITE_OK;
|
| }
|
|
|
| /* shared can just be set because we always have an exclusive */
|
| - if (locktype==SHARED_LOCK) {
|
| - pFile->locktype = locktype;
|
| + if (eFileLock==SHARED_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| return SQLITE_OK;
|
| }
|
|
|
| /* no, really, unlock. */
|
| - int rc = flock(pFile->h, LOCK_UN);
|
| - if (rc) {
|
| - int r, tErrno = errno;
|
| - r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| - if( IS_LOCK_ERROR(r) ){
|
| - pFile->lastErrno = tErrno;
|
| - }
|
| + if( robust_flock(pFile->h, LOCK_UN) ){
|
| #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| - if( (r & SQLITE_IOERR) == SQLITE_IOERR ){
|
| - r = SQLITE_BUSY;
|
| - }
|
| + return SQLITE_OK;
|
| #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
| -
|
| - return r;
|
| - } else {
|
| - pFile->locktype = NO_LOCK;
|
| + return SQLITE_IOERR_UNLOCK;
|
| + }else{
|
| + pFile->eFileLock = NO_LOCK;
|
| return SQLITE_OK;
|
| }
|
| }
|
| @@ -2188,13 +2257,13 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| assert( pFile );
|
|
|
| /* Check if a thread in this process holds such a lock */
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| reserved = 1;
|
| }
|
|
|
| /* Otherwise see if some other process holds it. */
|
| if( !reserved ){
|
| - sem_t *pSem = pFile->pOpen->pSem;
|
| + sem_t *pSem = pFile->pInode->pSem;
|
| struct stat statBuf;
|
|
|
| if( sem_trywait(pSem)==-1 ){
|
| @@ -2204,21 +2273,21 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| pFile->lastErrno = tErrno;
|
| } else {
|
| /* someone else has the lock when we are in NO_LOCK */
|
| - reserved = (pFile->locktype < SHARED_LOCK);
|
| + reserved = (pFile->eFileLock < SHARED_LOCK);
|
| }
|
| }else{
|
| /* we could have it if we want it */
|
| sem_post(pSem);
|
| }
|
| }
|
| - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved));
|
|
|
| *pResOut = reserved;
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -2246,16 +2315,16 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| ** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| ** routine to lower a locking level.
|
| */
|
| -static int semLock(sqlite3_file *id, int locktype) {
|
| +static int semLock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| int fd;
|
| - sem_t *pSem = pFile->pOpen->pSem;
|
| + sem_t *pSem = pFile->pInode->pSem;
|
| int rc = SQLITE_OK;
|
|
|
| /* if we already have a lock, it is exclusive.
|
| ** Just adjust level and punt on outta here. */
|
| - if (pFile->locktype > NO_LOCK) {
|
| - pFile->locktype = locktype;
|
| + if (pFile->eFileLock > NO_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| rc = SQLITE_OK;
|
| goto sem_end_lock;
|
| }
|
| @@ -2267,37 +2336,37 @@ static int semLock(sqlite3_file *id, int locktype) {
|
| }
|
|
|
| /* got it, set the type and return ok */
|
| - pFile->locktype = locktype;
|
| + pFile->eFileLock = eFileLock;
|
|
|
| sem_end_lock:
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| ** the requested locking level, this routine is a no-op.
|
| */
|
| -static int semUnlock(sqlite3_file *id, int locktype) {
|
| +static int semUnlock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| - sem_t *pSem = pFile->pOpen->pSem;
|
| + sem_t *pSem = pFile->pInode->pSem;
|
|
|
| assert( pFile );
|
| assert( pSem );
|
| - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
|
| - pFile->locktype, getpid());
|
| - assert( locktype<=SHARED_LOCK );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, getpid()));
|
| + assert( eFileLock<=SHARED_LOCK );
|
|
|
| /* no-op if possible */
|
| - if( pFile->locktype==locktype ){
|
| + if( pFile->eFileLock==eFileLock ){
|
| return SQLITE_OK;
|
| }
|
|
|
| /* shared can just be set because we always have an exclusive */
|
| - if (locktype==SHARED_LOCK) {
|
| - pFile->locktype = locktype;
|
| + if (eFileLock==SHARED_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -2310,7 +2379,7 @@ static int semUnlock(sqlite3_file *id, int locktype) {
|
| }
|
| return rc;
|
| }
|
| - pFile->locktype = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -2323,8 +2392,7 @@ static int semClose(sqlite3_file *id) {
|
| semUnlock(id, NO_LOCK);
|
| assert( pFile );
|
| unixEnterMutex();
|
| - releaseLockInfo(pFile->pLock);
|
| - releaseOpenCnt(pFile->pOpen);
|
| + releaseInodeInfo(pFile);
|
| unixLeaveMutex();
|
| closeUnixFile(id);
|
| }
|
| @@ -2355,7 +2423,7 @@ static int semClose(sqlite3_file *id) {
|
| */
|
| typedef struct afpLockingContext afpLockingContext;
|
| struct afpLockingContext {
|
| - unsigned long long sharedByte;
|
| + int reserved;
|
| const char *dbPath; /* Name of the open file */
|
| };
|
|
|
| @@ -2393,15 +2461,15 @@ static int afpSetLock(
|
| pb.length = length;
|
| pb.fd = pFile->h;
|
|
|
| - OSTRACE6("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n",
|
| + OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n",
|
| (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""),
|
| - offset, length);
|
| + offset, length));
|
| err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0);
|
| if ( err==-1 ) {
|
| int rc;
|
| int tErrno = errno;
|
| - OSTRACE4("AFPSETLOCK failed to fsctl() '%s' %d %s\n",
|
| - path, tErrno, strerror(tErrno));
|
| + OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n",
|
| + path, tErrno, strerror(tErrno)));
|
| #ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS
|
| rc = SQLITE_BUSY;
|
| #else
|
| @@ -2432,9 +2500,14 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
|
|
|
| assert( pFile );
|
| afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
| + if( context->reserved ){
|
| + *pResOut = 1;
|
| + return SQLITE_OK;
|
| + }
|
| + unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
|
|
| /* Check if a thread in this process holds such a lock */
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| + if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
| reserved = 1;
|
| }
|
|
|
| @@ -2456,14 +2529,15 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| }
|
| }
|
|
|
| - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
|
| + unixLeaveMutex();
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved));
|
|
|
| *pResOut = reserved;
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -2486,49 +2560,72 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| ** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| ** routine to lower a locking level.
|
| */
|
| -static int afpLock(sqlite3_file *id, int locktype){
|
| +static int afpLock(sqlite3_file *id, int eFileLock){
|
| int rc = SQLITE_OK;
|
| unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
|
|
| assert( pFile );
|
| - OSTRACE5("LOCK %d %s was %s pid=%d\n", pFile->h,
|
| - locktypeName(locktype), locktypeName(pFile->locktype), getpid());
|
| + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h,
|
| + azFileLock(eFileLock), azFileLock(pFile->eFileLock),
|
| + azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
|
|
|
| /* If there is already a lock of this type or more restrictive on the
|
| ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
|
| ** unixEnterMutex() hasn't been called yet.
|
| */
|
| - if( pFile->locktype>=locktype ){
|
| - OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h,
|
| - locktypeName(locktype));
|
| + if( pFile->eFileLock>=eFileLock ){
|
| + OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h,
|
| + azFileLock(eFileLock)));
|
| return SQLITE_OK;
|
| }
|
|
|
| /* Make sure the locking sequence is correct
|
| + ** (1) We never move from unlocked to anything higher than shared lock.
|
| + ** (2) SQLite never explicitly requests a pendig lock.
|
| + ** (3) A shared lock is always held when a reserve lock is requested.
|
| */
|
| - assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
|
| - assert( locktype!=PENDING_LOCK );
|
| - assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
|
| + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
|
| + assert( eFileLock!=PENDING_LOCK );
|
| + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
|
|
|
| - /* This mutex is needed because pFile->pLock is shared across threads
|
| + /* This mutex is needed because pFile->pInode is shared across threads
|
| */
|
| unixEnterMutex();
|
| + pInode = pFile->pInode;
|
|
|
| - /* Make sure the current thread owns the pFile.
|
| + /* If some thread using this PID has a lock via a different unixFile*
|
| + ** handle that precludes the requested lock, return BUSY.
|
| */
|
| - rc = transferOwnership(pFile);
|
| - if( rc!=SQLITE_OK ){
|
| - unixLeaveMutex();
|
| - return rc;
|
| + if( (pFile->eFileLock!=pInode->eFileLock &&
|
| + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
|
| + ){
|
| + rc = SQLITE_BUSY;
|
| + goto afp_end_lock;
|
| + }
|
| +
|
| + /* If a SHARED lock is requested, and some thread using this PID already
|
| + ** has a SHARED or RESERVED lock, then increment reference counts and
|
| + ** return SQLITE_OK.
|
| + */
|
| + if( eFileLock==SHARED_LOCK &&
|
| + (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
|
| + assert( eFileLock==SHARED_LOCK );
|
| + assert( pFile->eFileLock==0 );
|
| + assert( pInode->nShared>0 );
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nShared++;
|
| + pInode->nLock++;
|
| + goto afp_end_lock;
|
| }
|
|
|
| /* A PENDING lock is needed before acquiring a SHARED lock and before
|
| ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
|
| ** be released.
|
| */
|
| - if( locktype==SHARED_LOCK
|
| - || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK)
|
| + if( eFileLock==SHARED_LOCK
|
| + || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
|
| ){
|
| int failed;
|
| failed = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 1);
|
| @@ -2541,15 +2638,20 @@ static int afpLock(sqlite3_file *id, int locktype){
|
| /* If control gets to this point, then actually go ahead and make
|
| ** operating system calls for the specified lock.
|
| */
|
| - if( locktype==SHARED_LOCK ){
|
| - int lk, lrc1, lrc2, lrc1Errno;
|
| + if( eFileLock==SHARED_LOCK ){
|
| + int lrc1, lrc2, lrc1Errno;
|
| + long lk, mask;
|
|
|
| + assert( pInode->nShared==0 );
|
| + assert( pInode->eFileLock==0 );
|
| +
|
| + mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff;
|
| /* Now get the read-lock SHARED_LOCK */
|
| /* note that the quality of the randomness doesn't matter that much */
|
| lk = random();
|
| - context->sharedByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
|
| + pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1);
|
| lrc1 = afpSetLock(context->dbPath, pFile,
|
| - SHARED_FIRST+context->sharedByte, 1, 1);
|
| + SHARED_FIRST+pInode->sharedByte, 1, 1);
|
| if( IS_LOCK_ERROR(lrc1) ){
|
| lrc1Errno = pFile->lastErrno;
|
| }
|
| @@ -2566,34 +2668,42 @@ static int afpLock(sqlite3_file *id, int locktype){
|
| } else if( lrc1 != SQLITE_OK ) {
|
| rc = lrc1;
|
| } else {
|
| - pFile->locktype = SHARED_LOCK;
|
| - pFile->pOpen->nLock++;
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nLock++;
|
| + pInode->nShared = 1;
|
| }
|
| + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
|
| + /* We are trying for an exclusive lock but another thread in this
|
| + ** same process is still holding a shared lock. */
|
| + rc = SQLITE_BUSY;
|
| }else{
|
| /* The request was for a RESERVED or EXCLUSIVE lock. It is
|
| ** assumed that there is a SHARED or greater lock on the file
|
| ** already.
|
| */
|
| int failed = 0;
|
| - assert( 0!=pFile->locktype );
|
| - if (locktype >= RESERVED_LOCK && pFile->locktype < RESERVED_LOCK) {
|
| + assert( 0!=pFile->eFileLock );
|
| + if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) {
|
| /* Acquire a RESERVED lock */
|
| failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
|
| + if( !failed ){
|
| + context->reserved = 1;
|
| + }
|
| }
|
| - if (!failed && locktype == EXCLUSIVE_LOCK) {
|
| + if (!failed && eFileLock == EXCLUSIVE_LOCK) {
|
| /* Acquire an EXCLUSIVE lock */
|
|
|
| /* Remove the shared lock before trying the range. we'll need to
|
| ** reestablish the shared lock if we can't get the afpUnlock
|
| */
|
| if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST +
|
| - context->sharedByte, 1, 0)) ){
|
| + pInode->sharedByte, 1, 0)) ){
|
| int failed2 = SQLITE_OK;
|
| /* now attemmpt to get the exclusive lock range */
|
| failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST,
|
| SHARED_SIZE, 1);
|
| if( failed && (failed2 = afpSetLock(context->dbPath, pFile,
|
| - SHARED_FIRST + context->sharedByte, 1, 1)) ){
|
| + SHARED_FIRST + pInode->sharedByte, 1, 1)) ){
|
| /* Can't reestablish the shared lock. Sqlite can't deal, this is
|
| ** a critical I/O error
|
| */
|
| @@ -2611,78 +2721,124 @@ static int afpLock(sqlite3_file *id, int locktype){
|
| }
|
|
|
| if( rc==SQLITE_OK ){
|
| - pFile->locktype = locktype;
|
| - }else if( locktype==EXCLUSIVE_LOCK ){
|
| - pFile->locktype = PENDING_LOCK;
|
| + pFile->eFileLock = eFileLock;
|
| + pInode->eFileLock = eFileLock;
|
| + }else if( eFileLock==EXCLUSIVE_LOCK ){
|
| + pFile->eFileLock = PENDING_LOCK;
|
| + pInode->eFileLock = PENDING_LOCK;
|
| }
|
|
|
| afp_end_lock:
|
| unixLeaveMutex();
|
| - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
|
| - rc==SQLITE_OK ? "ok" : "failed");
|
| + OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| ** the requested locking level, this routine is a no-op.
|
| */
|
| -static int afpUnlock(sqlite3_file *id, int locktype) {
|
| +static int afpUnlock(sqlite3_file *id, int eFileLock) {
|
| int rc = SQLITE_OK;
|
| unixFile *pFile = (unixFile*)id;
|
| - afpLockingContext *pCtx = (afpLockingContext *) pFile->lockingContext;
|
| + unixInodeInfo *pInode;
|
| + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
| + int skipShared = 0;
|
| +#ifdef SQLITE_TEST
|
| + int h = pFile->h;
|
| +#endif
|
|
|
| assert( pFile );
|
| - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
|
| - pFile->locktype, getpid());
|
| + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
|
| + getpid()));
|
|
|
| - assert( locktype<=SHARED_LOCK );
|
| - if( pFile->locktype<=locktype ){
|
| + assert( eFileLock<=SHARED_LOCK );
|
| + if( pFile->eFileLock<=eFileLock ){
|
| return SQLITE_OK;
|
| }
|
| - if( CHECK_THREADID(pFile) ){
|
| - return SQLITE_MISUSE;
|
| - }
|
| unixEnterMutex();
|
| - if( pFile->locktype>SHARED_LOCK ){
|
| + pInode = pFile->pInode;
|
| + assert( pInode->nShared!=0 );
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + assert( pInode->eFileLock==pFile->eFileLock );
|
| + SimulateIOErrorBenign(1);
|
| + SimulateIOError( h=(-1) )
|
| + SimulateIOErrorBenign(0);
|
|
|
| - if( pFile->locktype==EXCLUSIVE_LOCK ){
|
| - rc = afpSetLock(pCtx->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
|
| - if( rc==SQLITE_OK && locktype==SHARED_LOCK ){
|
| +#ifndef NDEBUG
|
| + /* When reducing a lock such that other processes can start
|
| + ** reading the database file again, make sure that the
|
| + ** transaction counter was updated if any part of the database
|
| + ** file changed. If the transaction counter is not updated,
|
| + ** other connections to the same file might not realize that
|
| + ** the file has changed and hence might not know to flush their
|
| + ** cache. The use of a stale cache can lead to database corruption.
|
| + */
|
| + assert( pFile->inNormalWrite==0
|
| + || pFile->dbUpdate==0
|
| + || pFile->transCntrChng==1 );
|
| + pFile->inNormalWrite = 0;
|
| +#endif
|
| +
|
| + if( pFile->eFileLock==EXCLUSIVE_LOCK ){
|
| + rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
|
| + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){
|
| /* only re-establish the shared lock if necessary */
|
| - int sharedLockByte = SHARED_FIRST+pCtx->sharedByte;
|
| - rc = afpSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 1);
|
| + int sharedLockByte = SHARED_FIRST+pInode->sharedByte;
|
| + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1);
|
| + } else {
|
| + skipShared = 1;
|
| }
|
| }
|
| - if( rc==SQLITE_OK && pFile->locktype>=PENDING_LOCK ){
|
| - rc = afpSetLock(pCtx->dbPath, pFile, PENDING_BYTE, 1, 0);
|
| + if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){
|
| + rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
|
| }
|
| - if( rc==SQLITE_OK && pFile->locktype>=RESERVED_LOCK ){
|
| - rc = afpSetLock(pCtx->dbPath, pFile, RESERVED_BYTE, 1, 0);
|
| + if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){
|
| + rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0);
|
| + if( !rc ){
|
| + context->reserved = 0;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){
|
| + pInode->eFileLock = SHARED_LOCK;
|
| }
|
| - }else if( locktype==NO_LOCK ){
|
| - /* clear the shared lock */
|
| - int sharedLockByte = SHARED_FIRST+pCtx->sharedByte;
|
| - rc = afpSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 0);
|
| }
|
| + if( rc==SQLITE_OK && eFileLock==NO_LOCK ){
|
|
|
| - if( rc==SQLITE_OK ){
|
| - if( locktype==NO_LOCK ){
|
| - struct unixOpenCnt *pOpen = pFile->pOpen;
|
| - pOpen->nLock--;
|
| - assert( pOpen->nLock>=0 );
|
| - if( pOpen->nLock==0 ){
|
| - rc = closePendingFds(pFile);
|
| + /* Decrement the shared lock counter. Release the lock using an
|
| + ** OS call only when all threads in this same process have released
|
| + ** the lock.
|
| + */
|
| + unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
|
| + pInode->nShared--;
|
| + if( pInode->nShared==0 ){
|
| + SimulateIOErrorBenign(1);
|
| + SimulateIOError( h=(-1) )
|
| + SimulateIOErrorBenign(0);
|
| + if( !skipShared ){
|
| + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
|
| + }
|
| + if( !rc ){
|
| + pInode->eFileLock = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + pInode->nLock--;
|
| + assert( pInode->nLock>=0 );
|
| + if( pInode->nLock==0 ){
|
| + closePendingFds(pFile);
|
| }
|
| }
|
| }
|
| +
|
| unixLeaveMutex();
|
| - if( rc==SQLITE_OK ){
|
| - pFile->locktype = locktype;
|
| - }
|
| + if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
| return rc;
|
| }
|
|
|
| @@ -2690,24 +2846,25 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
|
| ** Close a file & cleanup AFP specific locking context
|
| */
|
| static int afpClose(sqlite3_file *id) {
|
| + int rc = SQLITE_OK;
|
| if( id ){
|
| unixFile *pFile = (unixFile*)id;
|
| afpUnlock(id, NO_LOCK);
|
| unixEnterMutex();
|
| - if( pFile->pOpen && pFile->pOpen->nLock ){
|
| + if( pFile->pInode && pFile->pInode->nLock ){
|
| /* If there are outstanding locks, do not actually close the file just
|
| ** yet because that would clear those locks. Instead, add the file
|
| - ** descriptor to pOpen->aPending. It will be automatically closed when
|
| + ** descriptor to pInode->aPending. It will be automatically closed when
|
| ** the last lock is cleared.
|
| */
|
| setPendingFd(pFile);
|
| }
|
| - releaseOpenCnt(pFile->pOpen);
|
| + releaseInodeInfo(pFile);
|
| sqlite3_free(pFile->lockingContext);
|
| - closeUnixFile(id);
|
| + rc = closeUnixFile(id);
|
| unixLeaveMutex();
|
| }
|
| - return SQLITE_OK;
|
| + return rc;
|
| }
|
|
|
| #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| @@ -2720,6 +2877,29 @@ static int afpClose(sqlite3_file *id) {
|
| ********************* End of the AFP lock implementation **********************
|
| ******************************************************************************/
|
|
|
| +/******************************************************************************
|
| +*************************** Begin NFS Locking ********************************/
|
| +
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +/*
|
| + ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| + ** must be either NO_LOCK or SHARED_LOCK.
|
| + **
|
| + ** If the locking level of the file descriptor is already at or below
|
| + ** the requested locking level, this routine is a no-op.
|
| + */
|
| +static int nfsUnlock(sqlite3_file *id, int eFileLock){
|
| + return posixUnlock(id, eFileLock, 1);
|
| +}
|
| +
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| +/*
|
| +** The code above is the NFS lock implementation. The code is specific
|
| +** to MacOSX and does not work on other unix platforms. No alternative
|
| +** is available.
|
| +**
|
| +********************* End of the NFS lock implementation **********************
|
| +******************************************************************************/
|
|
|
| /******************************************************************************
|
| **************** Non-locking sqlite3_file methods *****************************
|
| @@ -2746,13 +2926,15 @@ static int afpClose(sqlite3_file *id) {
|
| */
|
| static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
| int got;
|
| +#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
| i64 newOffset;
|
| +#endif
|
| TIMER_START;
|
| #if defined(USE_PREAD)
|
| - got = pread(id->h, pBuf, cnt, offset);
|
| + do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
|
| SimulateIOError( got = -1 );
|
| #elif defined(USE_PREAD64)
|
| - got = pread64(id->h, pBuf, cnt, offset);
|
| + do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR);
|
| SimulateIOError( got = -1 );
|
| #else
|
| newOffset = lseek(id->h, offset, SEEK_SET);
|
| @@ -2765,13 +2947,13 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
| }
|
| return -1;
|
| }
|
| - got = read(id->h, pBuf, cnt);
|
| + do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
|
| #endif
|
| TIMER_END;
|
| if( got<0 ){
|
| ((unixFile*)id)->lastErrno = errno;
|
| }
|
| - OSTRACE5("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED);
|
| + OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
|
| return got;
|
| }
|
|
|
| @@ -2792,10 +2974,12 @@ static int unixRead(
|
|
|
| /* If this is a database file (not a journal, master-journal or temp
|
| ** file), the bytes in the locking range should never be read or written. */
|
| +#if 0
|
| assert( pFile->pUnused==0
|
| || offset>=PENDING_BYTE+512
|
| || offset+amt<=PENDING_BYTE
|
| );
|
| +#endif
|
|
|
| got = seekAndRead(pFile, offset, pBuf, amt);
|
| if( got==amt ){
|
| @@ -2820,14 +3004,17 @@ static int unixRead(
|
| */
|
| static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
|
| int got;
|
| +#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
| i64 newOffset;
|
| +#endif
|
| TIMER_START;
|
| #if defined(USE_PREAD)
|
| - got = pwrite(id->h, pBuf, cnt, offset);
|
| + do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
|
| #elif defined(USE_PREAD64)
|
| - got = pwrite64(id->h, pBuf, cnt, offset);
|
| + do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
|
| #else
|
| newOffset = lseek(id->h, offset, SEEK_SET);
|
| + SimulateIOError( newOffset-- );
|
| if( newOffset!=offset ){
|
| if( newOffset == -1 ){
|
| ((unixFile*)id)->lastErrno = errno;
|
| @@ -2836,14 +3023,14 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
|
| }
|
| return -1;
|
| }
|
| - got = write(id->h, pBuf, cnt);
|
| + do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
|
| #endif
|
| TIMER_END;
|
| if( got<0 ){
|
| ((unixFile*)id)->lastErrno = errno;
|
| }
|
|
|
| - OSTRACE5("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED);
|
| + OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
|
| return got;
|
| }
|
|
|
| @@ -2865,10 +3052,12 @@ static int unixWrite(
|
|
|
| /* If this is a database file (not a journal, master-journal or temp
|
| ** file), the bytes in the locking range should never be read or written. */
|
| +#if 0
|
| assert( pFile->pUnused==0
|
| || offset>=PENDING_BYTE+512
|
| || offset+amt<=PENDING_BYTE
|
| );
|
| +#endif
|
|
|
| #ifndef NDEBUG
|
| /* If we are doing a normal write to a database file (as opposed to
|
| @@ -2899,6 +3088,7 @@ static int unixWrite(
|
| }
|
| SimulateIOError(( wrote=(-1), amt=1 ));
|
| SimulateDiskfullError(( wrote=0, amt=1 ));
|
| +
|
| if( amt>0 ){
|
| if( wrote<0 ){
|
| /* lastErrno set by seekAndWrite */
|
| @@ -2908,6 +3098,7 @@ static int unixWrite(
|
| return SQLITE_FULL;
|
| }
|
| }
|
| +
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -3000,7 +3191,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
| rc = SQLITE_OK;
|
| #elif HAVE_FULLFSYNC
|
| if( fullSync ){
|
| - rc = fcntl(fd, F_FULLFSYNC, 0);
|
| + rc = osFcntl(fd, F_FULLFSYNC, 0);
|
| }else{
|
| rc = 1;
|
| }
|
| @@ -3014,6 +3205,11 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
| */
|
| if( rc ) rc = fsync(fd);
|
|
|
| +#elif defined(__APPLE__)
|
| + /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
|
| + ** so currently we default to the macro that redefines fdatasync to fsync
|
| + */
|
| + rc = fsync(fd);
|
| #else
|
| rc = fdatasync(fd);
|
| #if OS_VXWORKS
|
| @@ -3062,17 +3258,16 @@ static int unixSync(sqlite3_file *id, int flags){
|
| SimulateDiskfullError( return SQLITE_FULL );
|
|
|
| assert( pFile );
|
| - OSTRACE2("SYNC %-3d\n", pFile->h);
|
| + OSTRACE(("SYNC %-3d\n", pFile->h));
|
| rc = full_fsync(pFile->h, isFullsync, isDataOnly);
|
| SimulateIOError( rc=1 );
|
| if( rc ){
|
| pFile->lastErrno = errno;
|
| - return SQLITE_IOERR_FSYNC;
|
| + return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
|
| }
|
| if( pFile->dirfd>=0 ){
|
| - int err;
|
| - OSTRACE4("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
|
| - HAVE_FULLFSYNC, isFullsync);
|
| + OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
|
| + HAVE_FULLFSYNC, isFullsync));
|
| #ifndef SQLITE_DISABLE_DIRSYNC
|
| /* The directory sync is only attempted if full_fsync is
|
| ** turned off or unavailable. If a full_fsync occurred above,
|
| @@ -3089,13 +3284,9 @@ static int unixSync(sqlite3_file *id, int flags){
|
| /* return SQLITE_IOERR; */
|
| }
|
| #endif
|
| - err = close(pFile->dirfd); /* Only need to sync once, so close the */
|
| - if( err==0 ){ /* directory when we are done */
|
| - pFile->dirfd = -1;
|
| - }else{
|
| - pFile->lastErrno = errno;
|
| - rc = SQLITE_IOERR_DIR_CLOSE;
|
| - }
|
| + /* Only need to sync once, so close the directory when we are done */
|
| + robust_close(pFile, pFile->dirfd, __LINE__);
|
| + pFile->dirfd = -1;
|
| }
|
| return rc;
|
| }
|
| @@ -3104,14 +3295,38 @@ static int unixSync(sqlite3_file *id, int flags){
|
| ** Truncate an open file to a specified size
|
| */
|
| static int unixTruncate(sqlite3_file *id, i64 nByte){
|
| + unixFile *pFile = (unixFile *)id;
|
| int rc;
|
| - assert( id );
|
| + assert( pFile );
|
| SimulateIOError( return SQLITE_IOERR_TRUNCATE );
|
| - rc = ftruncate(((unixFile*)id)->h, (off_t)nByte);
|
| +
|
| + /* If the user has configured a chunk-size for this file, truncate the
|
| + ** file so that it consists of an integer number of chunks (i.e. the
|
| + ** actual file size after the operation may be larger than the requested
|
| + ** size).
|
| + */
|
| + if( pFile->szChunk ){
|
| + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
| + }
|
| +
|
| + rc = robust_ftruncate(pFile->h, (off_t)nByte);
|
| if( rc ){
|
| - ((unixFile*)id)->lastErrno = errno;
|
| - return SQLITE_IOERR_TRUNCATE;
|
| + pFile->lastErrno = errno;
|
| + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
| }else{
|
| +#ifndef NDEBUG
|
| + /* If we are doing a normal write to a database file (as opposed to
|
| + ** doing a hot-journal rollback or a write to some file other than a
|
| + ** normal database file) and we truncate the file to zero length,
|
| + ** that effectively updates the change counter. This might happen
|
| + ** when restoring a database using the backup API from a zero-length
|
| + ** source.
|
| + */
|
| + if( pFile->inNormalWrite && nByte==0 ){
|
| + pFile->transCntrChng = 1;
|
| + }
|
| +#endif
|
| +
|
| return SQLITE_OK;
|
| }
|
| }
|
| @@ -3123,7 +3338,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
|
| int rc;
|
| struct stat buf;
|
| assert( id );
|
| - rc = fstat(((unixFile*)id)->h, &buf);
|
| + rc = osFstat(((unixFile*)id)->h, &buf);
|
| SimulateIOError( rc=1 );
|
| if( rc!=0 ){
|
| ((unixFile*)id)->lastErrno = errno;
|
| @@ -3131,7 +3346,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
|
| }
|
| *pSize = buf.st_size;
|
|
|
| - /* When opening a zero-size database, the findLockInfo() procedure
|
| + /* When opening a zero-size database, the findInodeInfo() procedure
|
| ** writes a single byte into that file in order to work around a bug
|
| ** in the OS-X msdos filesystem. In order to avoid problems with upper
|
| ** layers, we need to report this file size as zero even though it is
|
| @@ -3151,6 +3366,59 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
|
| static int proxyFileControl(sqlite3_file*,int,void*);
|
| #endif
|
|
|
| +/*
|
| +** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
|
| +** file-control operation.
|
| +**
|
| +** If the user has configured a chunk-size for this file, it could be
|
| +** that the file needs to be extended at this point. Otherwise, the
|
| +** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
|
| +*/
|
| +static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
| + if( pFile->szChunk ){
|
| + i64 nSize; /* Required file size */
|
| + struct stat buf; /* Used to hold return values of fstat() */
|
| +
|
| + if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
|
| +
|
| + nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
|
| + if( nSize>(i64)buf.st_size ){
|
| +
|
| +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
|
| + /* The code below is handling the return value of osFallocate()
|
| + ** correctly. posix_fallocate() is defined to "returns zero on success,
|
| + ** or an error number on failure". See the manpage for details. */
|
| + int err;
|
| + do{
|
| + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
|
| + }while( err==EINTR );
|
| + if( err ) return SQLITE_IOERR_WRITE;
|
| +#else
|
| + /* If the OS does not have posix_fallocate(), fake it. First use
|
| + ** ftruncate() to set the file size, then write a single byte to
|
| + ** the last byte in each block within the extended region. This
|
| + ** is the same technique used by glibc to implement posix_fallocate()
|
| + ** on systems that do not have a real fallocate() system call.
|
| + */
|
| + int nBlk = buf.st_blksize; /* File-system block size */
|
| + i64 iWrite; /* Next offset to write to */
|
| +
|
| + if( robust_ftruncate(pFile->h, nSize) ){
|
| + pFile->lastErrno = errno;
|
| + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
| + }
|
| + iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
|
| + while( iWrite<nSize ){
|
| + int nWrite = seekAndWrite(pFile, iWrite, "", 1);
|
| + if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
| + iWrite += nBlk;
|
| + }
|
| +#endif
|
| + }
|
| + }
|
| +
|
| + return SQLITE_OK;
|
| +}
|
|
|
| /*
|
| ** Information and control of an open file handle.
|
| @@ -3158,13 +3426,20 @@ static int proxyFileControl(sqlite3_file*,int,void*);
|
| static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
| switch( op ){
|
| case SQLITE_FCNTL_LOCKSTATE: {
|
| - *(int*)pArg = ((unixFile*)id)->locktype;
|
| + *(int*)pArg = ((unixFile*)id)->eFileLock;
|
| return SQLITE_OK;
|
| }
|
| case SQLITE_LAST_ERRNO: {
|
| *(int*)pArg = ((unixFile*)id)->lastErrno;
|
| return SQLITE_OK;
|
| }
|
| + case SQLITE_FCNTL_CHUNK_SIZE: {
|
| + ((unixFile*)id)->szChunk = *(int *)pArg;
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_SIZE_HINT: {
|
| + return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
|
| + }
|
| #ifndef NDEBUG
|
| /* The pager calls this method to signal that it has done
|
| ** a rollback and that the database is therefore unchanged and
|
| @@ -3182,8 +3457,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
| return proxyFileControl(id,op,pArg);
|
| }
|
| #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
|
| + case SQLITE_FCNTL_SYNC_OMITTED: {
|
| + return SQLITE_OK; /* A no-op */
|
| + }
|
| }
|
| - return SQLITE_ERROR;
|
| + return SQLITE_NOTFOUND;
|
| }
|
|
|
| /*
|
| @@ -3209,6 +3487,645 @@ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
|
| return 0;
|
| }
|
|
|
| +#ifndef SQLITE_OMIT_WAL
|
| +
|
| +
|
| +/*
|
| +** Object used to represent an shared memory buffer.
|
| +**
|
| +** When multiple threads all reference the same wal-index, each thread
|
| +** has its own unixShm object, but they all point to a single instance
|
| +** of this unixShmNode object. In other words, each wal-index is opened
|
| +** only once per process.
|
| +**
|
| +** Each unixShmNode object is connected to a single unixInodeInfo object.
|
| +** We could coalesce this object into unixInodeInfo, but that would mean
|
| +** every open file that does not use shared memory (in other words, most
|
| +** open files) would have to carry around this extra information. So
|
| +** the unixInodeInfo object contains a pointer to this unixShmNode object
|
| +** and the unixShmNode object is created only when needed.
|
| +**
|
| +** unixMutexHeld() must be true when creating or destroying
|
| +** this object or while reading or writing the following fields:
|
| +**
|
| +** nRef
|
| +**
|
| +** The following fields are read-only after the object is created:
|
| +**
|
| +** fid
|
| +** zFilename
|
| +**
|
| +** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and
|
| +** unixMutexHeld() is true when reading or writing any other field
|
| +** in this structure.
|
| +*/
|
| +struct unixShmNode {
|
| + unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
|
| + sqlite3_mutex *mutex; /* Mutex to access this object */
|
| + char *zFilename; /* Name of the mmapped file */
|
| + int h; /* Open file descriptor */
|
| + int szRegion; /* Size of shared-memory regions */
|
| + int nRegion; /* Size of array apRegion */
|
| + char **apRegion; /* Array of mapped shared-memory regions */
|
| + int nRef; /* Number of unixShm objects pointing to this */
|
| + unixShm *pFirst; /* All unixShm objects pointing to this */
|
| +#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
|
| +};
|
| +
|
| +/*
|
| +** Structure used internally by this VFS to record the state of an
|
| +** open shared memory connection.
|
| +**
|
| +** The following fields are initialized when this object is created and
|
| +** are read-only thereafter:
|
| +**
|
| +** unixShm.pFile
|
| +** unixShm.id
|
| +**
|
| +** All other fields are read/write. The unixShm.pFile->mutex must be held
|
| +** while accessing any read/write fields.
|
| +*/
|
| +struct unixShm {
|
| + unixShmNode *pShmNode; /* The underlying unixShmNode object */
|
| + unixShm *pNext; /* Next unixShm with the same unixShmNode */
|
| + u8 hasMutex; /* True if holding the unixShmNode mutex */
|
| + u16 sharedMask; /* Mask of shared locks held */
|
| + u16 exclMask; /* Mask of exclusive locks held */
|
| +#ifdef SQLITE_DEBUG
|
| + u8 id; /* Id of this connection within its unixShmNode */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** Constants used for locking
|
| +*/
|
| +#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
|
| +#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
|
| +
|
| +/*
|
| +** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
|
| +**
|
| +** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
|
| +** otherwise.
|
| +*/
|
| +static int unixShmSystemLock(
|
| + unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */
|
| + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
|
| + int ofst, /* First byte of the locking range */
|
| + int n /* Number of bytes to lock */
|
| +){
|
| + struct flock f; /* The posix advisory locking structure */
|
| + int rc = SQLITE_OK; /* Result code form fcntl() */
|
| +
|
| + /* Access to the unixShmNode object is serialized by the caller */
|
| + assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
|
| +
|
| + /* Shared locks never span more than one byte */
|
| + assert( n==1 || lockType!=F_RDLCK );
|
| +
|
| + /* Locks are within range */
|
| + assert( n>=1 && n<SQLITE_SHM_NLOCK );
|
| +
|
| + if( pShmNode->h>=0 ){
|
| + /* Initialize the locking parameters */
|
| + memset(&f, 0, sizeof(f));
|
| + f.l_type = lockType;
|
| + f.l_whence = SEEK_SET;
|
| + f.l_start = ofst;
|
| + f.l_len = n;
|
| +
|
| + rc = osFcntl(pShmNode->h, F_SETLK, &f);
|
| + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
|
| + }
|
| +
|
| + /* Update the global lock state and do debug tracing */
|
| +#ifdef SQLITE_DEBUG
|
| + { u16 mask;
|
| + OSTRACE(("SHM-LOCK "));
|
| + mask = (1<<(ofst+n)) - (1<<ofst);
|
| + if( rc==SQLITE_OK ){
|
| + if( lockType==F_UNLCK ){
|
| + OSTRACE(("unlock %d ok", ofst));
|
| + pShmNode->exclMask &= ~mask;
|
| + pShmNode->sharedMask &= ~mask;
|
| + }else if( lockType==F_RDLCK ){
|
| + OSTRACE(("read-lock %d ok", ofst));
|
| + pShmNode->exclMask &= ~mask;
|
| + pShmNode->sharedMask |= mask;
|
| + }else{
|
| + assert( lockType==F_WRLCK );
|
| + OSTRACE(("write-lock %d ok", ofst));
|
| + pShmNode->exclMask |= mask;
|
| + pShmNode->sharedMask &= ~mask;
|
| + }
|
| + }else{
|
| + if( lockType==F_UNLCK ){
|
| + OSTRACE(("unlock %d failed", ofst));
|
| + }else if( lockType==F_RDLCK ){
|
| + OSTRACE(("read-lock failed"));
|
| + }else{
|
| + assert( lockType==F_WRLCK );
|
| + OSTRACE(("write-lock %d failed", ofst));
|
| + }
|
| + }
|
| + OSTRACE((" - afterwards %03x,%03x\n",
|
| + pShmNode->sharedMask, pShmNode->exclMask));
|
| + }
|
| +#endif
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
|
| +**
|
| +** This is not a VFS shared-memory method; it is a utility function called
|
| +** by VFS shared-memory methods.
|
| +*/
|
| +static void unixShmPurge(unixFile *pFd){
|
| + unixShmNode *p = pFd->pInode->pShmNode;
|
| + assert( unixMutexHeld() );
|
| + if( p && p->nRef==0 ){
|
| + int i;
|
| + assert( p->pInode==pFd->pInode );
|
| + if( p->mutex ) sqlite3_mutex_free(p->mutex);
|
| + for(i=0; i<p->nRegion; i++){
|
| + if( p->h>=0 ){
|
| + munmap(p->apRegion[i], p->szRegion);
|
| + }else{
|
| + sqlite3_free(p->apRegion[i]);
|
| + }
|
| + }
|
| + sqlite3_free(p->apRegion);
|
| + if( p->h>=0 ){
|
| + robust_close(pFd, p->h, __LINE__);
|
| + p->h = -1;
|
| + }
|
| + p->pInode->pShmNode = 0;
|
| + sqlite3_free(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Open a shared-memory area associated with open database file pDbFd.
|
| +** This particular implementation uses mmapped files.
|
| +**
|
| +** The file used to implement shared-memory is in the same directory
|
| +** as the open database file and has the same name as the open database
|
| +** file with the "-shm" suffix added. For example, if the database file
|
| +** is "/home/user1/config.db" then the file that is created and mmapped
|
| +** for shared memory will be called "/home/user1/config.db-shm".
|
| +**
|
| +** Another approach to is to use files in /dev/shm or /dev/tmp or an
|
| +** some other tmpfs mount. But if a file in a different directory
|
| +** from the database file is used, then differing access permissions
|
| +** or a chroot() might cause two different processes on the same
|
| +** database to end up using different files for shared memory -
|
| +** meaning that their memory would not really be shared - resulting
|
| +** in database corruption. Nevertheless, this tmpfs file usage
|
| +** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm"
|
| +** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time
|
| +** option results in an incompatible build of SQLite; builds of SQLite
|
| +** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the
|
| +** same database file at the same time, database corruption will likely
|
| +** result. The SQLITE_SHM_DIRECTORY compile-time option is considered
|
| +** "unsupported" and may go away in a future SQLite release.
|
| +**
|
| +** 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.
|
| +**
|
| +** If the original database file (pDbFd) is using the "unix-excl" VFS
|
| +** that means that an exclusive lock is held on the database file and
|
| +** that no other processes are able to read or write the database. In
|
| +** that case, we do not really need shared memory. No shared memory
|
| +** file is created. The shared memory will be simulated with heap memory.
|
| +*/
|
| +static int unixOpenSharedMemory(unixFile *pDbFd){
|
| + struct unixShm *p = 0; /* The connection to be opened */
|
| + struct unixShmNode *pShmNode; /* The underlying mmapped file */
|
| + int rc; /* Result code */
|
| + unixInodeInfo *pInode; /* The inode of fd */
|
| + char *zShmFilename; /* Name of the file used for SHM */
|
| + int nShmFilename; /* Size of the SHM filename in bytes */
|
| +
|
| + /* Allocate space for the new unixShm object. */
|
| + p = sqlite3_malloc( sizeof(*p) );
|
| + if( p==0 ) return SQLITE_NOMEM;
|
| + memset(p, 0, sizeof(*p));
|
| + assert( pDbFd->pShm==0 );
|
| +
|
| + /* Check to see if a unixShmNode object already exists. Reuse an existing
|
| + ** one if present. Create a new one if necessary.
|
| + */
|
| + unixEnterMutex();
|
| + pInode = pDbFd->pInode;
|
| + pShmNode = pInode->pShmNode;
|
| + if( pShmNode==0 ){
|
| + struct stat sStat; /* fstat() info for database file */
|
| +
|
| + /* Call fstat() to figure out the permissions on the database file. If
|
| + ** a new *-shm file is created, an attempt will be made to create it
|
| + ** with the same permissions. The actual permissions the file is created
|
| + ** with are subject to the current umask setting.
|
| + */
|
| + if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
|
| + rc = SQLITE_IOERR_FSTAT;
|
| + goto shm_open_err;
|
| + }
|
| +
|
| +#ifdef SQLITE_SHM_DIRECTORY
|
| + nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
|
| +#else
|
| + nShmFilename = 5 + (int)strlen(pDbFd->zPath);
|
| +#endif
|
| + pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
|
| + if( pShmNode==0 ){
|
| + rc = SQLITE_NOMEM;
|
| + goto shm_open_err;
|
| + }
|
| + memset(pShmNode, 0, sizeof(*pShmNode));
|
| + zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
|
| +#ifdef SQLITE_SHM_DIRECTORY
|
| + sqlite3_snprintf(nShmFilename, zShmFilename,
|
| + SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
|
| + (u32)sStat.st_ino, (u32)sStat.st_dev);
|
| +#else
|
| + sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);
|
| +#endif
|
| + pShmNode->h = -1;
|
| + pDbFd->pInode->pShmNode = pShmNode;
|
| + pShmNode->pInode = pDbFd->pInode;
|
| + pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
| + if( pShmNode->mutex==0 ){
|
| + rc = SQLITE_NOMEM;
|
| + goto shm_open_err;
|
| + }
|
| +
|
| + if( pInode->bProcessLock==0 ){
|
| + pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
|
| + (sStat.st_mode & 0777));
|
| + if( pShmNode->h<0 ){
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
| + goto shm_open_err;
|
| + }
|
| +
|
| + /* Check to see if another process is holding the dead-man switch.
|
| + ** If not, truncate the file to zero length.
|
| + */
|
| + rc = SQLITE_OK;
|
| + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
|
| + if( robust_ftruncate(pShmNode->h, 0) ){
|
| + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1);
|
| + }
|
| + if( rc ) goto shm_open_err;
|
| + }
|
| + }
|
| +
|
| + /* Make the new connection a child of the unixShmNode */
|
| + p->pShmNode = pShmNode;
|
| +#ifdef SQLITE_DEBUG
|
| + p->id = pShmNode->nextShmId++;
|
| +#endif
|
| + pShmNode->nRef++;
|
| + pDbFd->pShm = p;
|
| + unixLeaveMutex();
|
| +
|
| + /* The reference count on pShmNode has already been incremented under
|
| + ** the cover of the unixEnterMutex() mutex and the pointer from the
|
| + ** new (struct unixShm) object to the pShmNode has been set. All that is
|
| + ** left to do is to link the new object into the linked list starting
|
| + ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
|
| + ** mutex.
|
| + */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + p->pNext = pShmNode->pFirst;
|
| + pShmNode->pFirst = p;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return SQLITE_OK;
|
| +
|
| + /* Jump here on any error */
|
| +shm_open_err:
|
| + unixShmPurge(pDbFd); /* This call frees pShmNode if required */
|
| + sqlite3_free(p);
|
| + unixLeaveMutex();
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** This function is called to obtain a pointer to region iRegion of the
|
| +** shared-memory associated with the database file fd. Shared-memory regions
|
| +** are numbered starting from zero. Each shared-memory region is szRegion
|
| +** bytes in size.
|
| +**
|
| +** If an error occurs, an error code is returned and *pp is set to NULL.
|
| +**
|
| +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
|
| +** region has not been allocated (by any client, including one running in a
|
| +** separate process), then *pp is set to NULL and SQLITE_OK returned. If
|
| +** bExtend is non-zero and the requested shared-memory region has not yet
|
| +** been allocated, it is allocated by this function.
|
| +**
|
| +** If the shared-memory region has already been allocated or is allocated by
|
| +** this call as described above, then it is mapped into this processes
|
| +** address space (if it is not already), *pp is set to point to the mapped
|
| +** memory and SQLITE_OK returned.
|
| +*/
|
| +static int unixShmMap(
|
| + sqlite3_file *fd, /* Handle open on database file */
|
| + int iRegion, /* Region to retrieve */
|
| + int szRegion, /* Size of regions */
|
| + int bExtend, /* True to extend file if necessary */
|
| + void volatile **pp /* OUT: Mapped memory */
|
| +){
|
| + unixFile *pDbFd = (unixFile*)fd;
|
| + unixShm *p;
|
| + unixShmNode *pShmNode;
|
| + int rc = SQLITE_OK;
|
| +
|
| + /* If the shared-memory file has not yet been opened, open it now. */
|
| + if( pDbFd->pShm==0 ){
|
| + rc = unixOpenSharedMemory(pDbFd);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + }
|
| +
|
| + p = pDbFd->pShm;
|
| + pShmNode = p->pShmNode;
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
| + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
| +
|
| + if( pShmNode->nRegion<=iRegion ){
|
| + char **apNew; /* New apRegion[] array */
|
| + int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
|
| + struct stat sStat; /* Used by fstat() */
|
| +
|
| + pShmNode->szRegion = szRegion;
|
| +
|
| + if( pShmNode->h>=0 ){
|
| + /* The requested region is not mapped into this processes address space.
|
| + ** Check to see if it has been allocated (i.e. if the wal-index file is
|
| + ** large enough to contain the requested region).
|
| + */
|
| + if( osFstat(pShmNode->h, &sStat) ){
|
| + rc = SQLITE_IOERR_SHMSIZE;
|
| + goto shmpage_out;
|
| + }
|
| +
|
| + if( sStat.st_size<nByte ){
|
| + /* The requested memory region does not exist. If bExtend is set to
|
| + ** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
|
| + **
|
| + ** Alternatively, if bExtend is true, use ftruncate() to allocate
|
| + ** the requested memory region.
|
| + */
|
| + if( !bExtend ) goto shmpage_out;
|
| + if( robust_ftruncate(pShmNode->h, nByte) ){
|
| + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate",
|
| + pShmNode->zFilename);
|
| + goto shmpage_out;
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* Map the requested memory region into this processes address space. */
|
| + apNew = (char **)sqlite3_realloc(
|
| + pShmNode->apRegion, (iRegion+1)*sizeof(char *)
|
| + );
|
| + if( !apNew ){
|
| + rc = SQLITE_IOERR_NOMEM;
|
| + goto shmpage_out;
|
| + }
|
| + pShmNode->apRegion = apNew;
|
| + while(pShmNode->nRegion<=iRegion){
|
| + void *pMem;
|
| + if( pShmNode->h>=0 ){
|
| + pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE,
|
| + MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
|
| + );
|
| + if( pMem==MAP_FAILED ){
|
| + rc = SQLITE_IOERR;
|
| + goto shmpage_out;
|
| + }
|
| + }else{
|
| + pMem = sqlite3_malloc(szRegion);
|
| + if( pMem==0 ){
|
| + rc = SQLITE_NOMEM;
|
| + goto shmpage_out;
|
| + }
|
| + memset(pMem, 0, szRegion);
|
| + }
|
| + pShmNode->apRegion[pShmNode->nRegion] = pMem;
|
| + pShmNode->nRegion++;
|
| + }
|
| + }
|
| +
|
| +shmpage_out:
|
| + if( pShmNode->nRegion>iRegion ){
|
| + *pp = pShmNode->apRegion[iRegion];
|
| + }else{
|
| + *pp = 0;
|
| + }
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Change the lock state for a shared-memory segment.
|
| +**
|
| +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
|
| +** different here than in posix. In xShmLock(), one can go from unlocked
|
| +** to shared and back or from unlocked to exclusive and back. But one may
|
| +** not go from shared to exclusive or from exclusive to shared.
|
| +*/
|
| +static int unixShmLock(
|
| + sqlite3_file *fd, /* Database file holding the shared memory */
|
| + int ofst, /* First lock to acquire or release */
|
| + int n, /* Number of locks to acquire or release */
|
| + int flags /* What to do with the lock */
|
| +){
|
| + unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
|
| + unixShm *p = pDbFd->pShm; /* The shared memory being locked */
|
| + unixShm *pX; /* For looping over all siblings */
|
| + unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
|
| + int rc = SQLITE_OK; /* Result code */
|
| + u16 mask; /* Mask of locks to take or release */
|
| +
|
| + assert( pShmNode==pDbFd->pInode->pShmNode );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
|
| + assert( n>=1 );
|
| + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
|
| + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
|
| + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
| + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
| +
|
| + mask = (1<<(ofst+n)) - (1<<ofst);
|
| + assert( n>1 || mask==(1<<ofst) );
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + if( flags & SQLITE_SHM_UNLOCK ){
|
| + u16 allMask = 0; /* Mask of locks held by siblings */
|
| +
|
| + /* See if any siblings hold this same lock */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( pX==p ) continue;
|
| + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
|
| + allMask |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Unlock the system-level locks */
|
| + if( (mask & allMask)==0 ){
|
| + rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| +
|
| + /* Undo the local locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->exclMask &= ~mask;
|
| + p->sharedMask &= ~mask;
|
| + }
|
| + }else if( flags & SQLITE_SHM_SHARED ){
|
| + u16 allShared = 0; /* Union of locks held by connections other than "p" */
|
| +
|
| + /* 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.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + allShared |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Get shared locks at the system level, if necessary */
|
| + if( rc==SQLITE_OK ){
|
| + if( (allShared & mask)==0 ){
|
| + rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| +
|
| + /* Get the local shared locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->sharedMask |= mask;
|
| + }
|
| + }else{
|
| + /* Make sure no sibling connections hold locks that will block this
|
| + ** lock. If any do, return SQLITE_BUSY right away.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* Get the exclusive locks at the system level. Then if successful
|
| + ** also mark the local connection as being locked.
|
| + */
|
| + if( rc==SQLITE_OK ){
|
| + rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n);
|
| + if( rc==SQLITE_OK ){
|
| + assert( (p->sharedMask & mask)==0 );
|
| + p->exclMask |= mask;
|
| + }
|
| + }
|
| + }
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
|
| + p->id, getpid(), p->sharedMask, p->exclMask));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Implement a memory barrier or memory fence on shared memory.
|
| +**
|
| +** All loads and stores begun before the barrier must complete before
|
| +** any load or store begun after the barrier.
|
| +*/
|
| +static void unixShmBarrier(
|
| + sqlite3_file *fd /* Database file holding the shared memory */
|
| +){
|
| + UNUSED_PARAMETER(fd);
|
| + unixEnterMutex();
|
| + unixLeaveMutex();
|
| +}
|
| +
|
| +/*
|
| +** Close a connection to shared-memory. Delete the underlying
|
| +** storage if deleteFlag is true.
|
| +**
|
| +** If there is no shared memory associated with the connection then this
|
| +** routine is a harmless no-op.
|
| +*/
|
| +static int unixShmUnmap(
|
| + sqlite3_file *fd, /* The underlying database file */
|
| + int deleteFlag /* Delete shared-memory if true */
|
| +){
|
| + unixShm *p; /* The connection to be closed */
|
| + unixShmNode *pShmNode; /* The underlying shared-memory file */
|
| + unixShm **pp; /* For looping over sibling connections */
|
| + unixFile *pDbFd; /* The underlying database file */
|
| +
|
| + pDbFd = (unixFile*)fd;
|
| + p = pDbFd->pShm;
|
| + if( p==0 ) return SQLITE_OK;
|
| + pShmNode = p->pShmNode;
|
| +
|
| + assert( pShmNode==pDbFd->pInode->pShmNode );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| +
|
| + /* Remove connection p from the set of connections associated
|
| + ** with pShmNode */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
|
| + *pp = p->pNext;
|
| +
|
| + /* Free the connection p */
|
| + sqlite3_free(p);
|
| + pDbFd->pShm = 0;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| +
|
| + /* If pShmNode->nRef has reached 0, then close the underlying
|
| + ** shared-memory file, too */
|
| + unixEnterMutex();
|
| + assert( pShmNode->nRef>0 );
|
| + pShmNode->nRef--;
|
| + if( pShmNode->nRef==0 ){
|
| + if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename);
|
| + unixShmPurge(pDbFd);
|
| + }
|
| + unixLeaveMutex();
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +
|
| +#else
|
| +# define unixShmMap 0
|
| +# define unixShmLock 0
|
| +# define unixShmBarrier 0
|
| +# define unixShmUnmap 0
|
| +#endif /* #ifndef SQLITE_OMIT_WAL */
|
| +
|
| /*
|
| ** Here ends the implementation of all sqlite3_file methods.
|
| **
|
| @@ -3249,9 +4166,9 @@ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
|
| ** * An I/O method finder function called FINDER that returns a pointer
|
| ** to the METHOD object in the previous bullet.
|
| */
|
| -#define IOMETHODS(FINDER, METHOD, CLOSE, LOCK, UNLOCK, CKLOCK) \
|
| +#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \
|
| static const sqlite3_io_methods METHOD = { \
|
| - 1, /* iVersion */ \
|
| + VERSION, /* iVersion */ \
|
| CLOSE, /* xClose */ \
|
| unixRead, /* xRead */ \
|
| unixWrite, /* xWrite */ \
|
| @@ -3263,7 +4180,11 @@ static const sqlite3_io_methods METHOD = { \
|
| CKLOCK, /* xCheckReservedLock */ \
|
| unixFileControl, /* xFileControl */ \
|
| unixSectorSize, /* xSectorSize */ \
|
| - unixDeviceCharacteristics /* xDeviceCapabilities */ \
|
| + unixDeviceCharacteristics, /* xDeviceCapabilities */ \
|
| + unixShmMap, /* xShmMap */ \
|
| + unixShmLock, /* xShmLock */ \
|
| + unixShmBarrier, /* xShmBarrier */ \
|
| + unixShmUnmap /* xShmUnmap */ \
|
| }; \
|
| static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \
|
| UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \
|
| @@ -3280,6 +4201,7 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \
|
| IOMETHODS(
|
| posixIoFinder, /* Finder function name */
|
| posixIoMethods, /* sqlite3_io_methods object name */
|
| + 2, /* shared memory is enabled */
|
| unixClose, /* xClose method */
|
| unixLock, /* xLock method */
|
| unixUnlock, /* xUnlock method */
|
| @@ -3288,6 +4210,7 @@ IOMETHODS(
|
| IOMETHODS(
|
| nolockIoFinder, /* Finder function name */
|
| nolockIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| nolockClose, /* xClose method */
|
| nolockLock, /* xLock method */
|
| nolockUnlock, /* xUnlock method */
|
| @@ -3296,6 +4219,7 @@ IOMETHODS(
|
| IOMETHODS(
|
| dotlockIoFinder, /* Finder function name */
|
| dotlockIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| dotlockClose, /* xClose method */
|
| dotlockLock, /* xLock method */
|
| dotlockUnlock, /* xUnlock method */
|
| @@ -3306,6 +4230,7 @@ IOMETHODS(
|
| IOMETHODS(
|
| flockIoFinder, /* Finder function name */
|
| flockIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| flockClose, /* xClose method */
|
| flockLock, /* xLock method */
|
| flockUnlock, /* xUnlock method */
|
| @@ -3317,6 +4242,7 @@ IOMETHODS(
|
| IOMETHODS(
|
| semIoFinder, /* Finder function name */
|
| semIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| semClose, /* xClose method */
|
| semLock, /* xLock method */
|
| semUnlock, /* xUnlock method */
|
| @@ -3328,6 +4254,7 @@ IOMETHODS(
|
| IOMETHODS(
|
| afpIoFinder, /* Finder function name */
|
| afpIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| afpClose, /* xClose method */
|
| afpLock, /* xLock method */
|
| afpUnlock, /* xUnlock method */
|
| @@ -3336,23 +4263,6 @@ IOMETHODS(
|
| #endif
|
|
|
| /*
|
| -** The "Whole File Locking" finder returns the same set of methods as
|
| -** the posix locking finder. But it also sets the SQLITE_WHOLE_FILE_LOCKING
|
| -** flag to force the posix advisory locks to cover the whole file instead
|
| -** of just a small span of bytes near the 1GiB boundary. Whole File Locking
|
| -** is useful on NFS-mounted files since it helps NFS to maintain cache
|
| -** coherency. But it is a detriment to other filesystems since it runs
|
| -** slower.
|
| -*/
|
| -static const sqlite3_io_methods *posixWflIoFinderImpl(const char*z, unixFile*p){
|
| - UNUSED_PARAMETER(z);
|
| - p->fileFlags = SQLITE_WHOLE_FILE_LOCKING;
|
| - return &posixIoMethods;
|
| -}
|
| -static const sqlite3_io_methods
|
| - *(*const posixWflIoFinder)(const char*,unixFile *p) = posixWflIoFinderImpl;
|
| -
|
| -/*
|
| ** 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
|
| @@ -3369,6 +4279,7 @@ static int proxyCheckReservedLock(sqlite3_file*, int*);
|
| IOMETHODS(
|
| proxyIoFinder, /* Finder function name */
|
| proxyIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| proxyClose, /* xClose method */
|
| proxyLock, /* xLock method */
|
| proxyUnlock, /* xUnlock method */
|
| @@ -3376,6 +4287,18 @@ IOMETHODS(
|
| )
|
| #endif
|
|
|
| +/* 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, /* shared memory is disabled */
|
| + unixClose, /* xClose method */
|
| + unixLock, /* xLock method */
|
| + nfsUnlock, /* xUnlock method */
|
| + unixCheckReservedLock /* xCheckReservedLock method */
|
| +)
|
| +#endif
|
|
|
| #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| /*
|
| @@ -3396,11 +4319,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
|
| { "hfs", &posixIoMethods },
|
| { "ufs", &posixIoMethods },
|
| { "afpfs", &afpIoMethods },
|
| -#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB
|
| { "smbfs", &afpIoMethods },
|
| -#else
|
| - { "smbfs", &flockIoMethods },
|
| -#endif
|
| { "webdav", &nolockIoMethods },
|
| { 0, 0 }
|
| };
|
| @@ -3432,9 +4351,12 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
|
| lockInfo.l_start = 0;
|
| lockInfo.l_whence = SEEK_SET;
|
| lockInfo.l_type = F_RDLCK;
|
| - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| - pNew->fileFlags = SQLITE_WHOLE_FILE_LOCKING;
|
| - return &posixIoMethods;
|
| + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| + if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){
|
| + return &nfsIoMethods;
|
| + } else {
|
| + return &posixIoMethods;
|
| + }
|
| }else{
|
| return &dotlockIoMethods;
|
| }
|
| @@ -3471,7 +4393,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
|
| lockInfo.l_start = 0;
|
| lockInfo.l_whence = SEEK_SET;
|
| lockInfo.l_type = F_RDLCK;
|
| - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| return &posixIoMethods;
|
| }else{
|
| return &semIoMethods;
|
| @@ -3512,25 +4434,43 @@ int fillInUnixFile(
|
| 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 */
|
| + int isDelete, /* Delete on close if true */
|
| + int isReadOnly /* True if the file is opened read-only */
|
| ){
|
| const sqlite3_io_methods *pLockingStyle;
|
| unixFile *pNew = (unixFile *)pId;
|
| int rc = SQLITE_OK;
|
|
|
| - assert( pNew->pLock==NULL );
|
| - assert( pNew->pOpen==NULL );
|
| + assert( pNew->pInode==NULL );
|
|
|
| /* Parameter isDelete is only used on vxworks. Express this explicitly
|
| ** here to prevent compiler warnings about unused parameters.
|
| */
|
| UNUSED_PARAMETER(isDelete);
|
|
|
| - OSTRACE3("OPEN %-3d %s\n", h, zFilename);
|
| + /* Usually the path zFilename should not be a relative pathname. The
|
| + ** exception is when opening the proxy "conch" file in builds that
|
| + ** include the special Apple locking styles.
|
| + */
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + assert( zFilename==0 || zFilename[0]=='/'
|
| + || pVfs->pAppData==(void*)&autolockIoFinder );
|
| +#else
|
| + assert( zFilename==0 || zFilename[0]=='/' );
|
| +#endif
|
| +
|
| + OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
| pNew->h = h;
|
| pNew->dirfd = dirfd;
|
| - SET_THREADID(pNew);
|
| - pNew->fileFlags = 0;
|
| + pNew->zPath = zFilename;
|
| + if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
|
| + pNew->ctrlFlags = UNIXFILE_EXCL;
|
| + }else{
|
| + pNew->ctrlFlags = 0;
|
| + }
|
| + if( isReadOnly ){
|
| + pNew->ctrlFlags |= UNIXFILE_RDONLY;
|
| + }
|
|
|
| #if OS_VXWORKS
|
| pNew->pId = vxworksFindFileId(zFilename);
|
| @@ -3552,12 +4492,16 @@ int fillInUnixFile(
|
| #endif
|
| }
|
|
|
| - if( pLockingStyle == &posixIoMethods ){
|
| + if( pLockingStyle == &posixIoMethods
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + || pLockingStyle == &nfsIoMethods
|
| +#endif
|
| + ){
|
| unixEnterMutex();
|
| - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen);
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| if( rc!=SQLITE_OK ){
|
| - /* If an error occured in findLockInfo(), close the file descriptor
|
| - ** immediately, before releasing the mutex. findLockInfo() may fail
|
| + /* If an error occured in findInodeInfo(), close the file descriptor
|
| + ** immediately, before releasing the mutex. findInodeInfo() may fail
|
| ** in two scenarios:
|
| **
|
| ** (a) A call to fstat() failed.
|
| @@ -3566,7 +4510,7 @@ int fillInUnixFile(
|
| ** 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
|
| + ** findInodeInfo(). 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.
|
| **
|
| @@ -3574,7 +4518,7 @@ int fillInUnixFile(
|
| ** 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);
|
| + robust_close(pNew, h, __LINE__);
|
| h = -1;
|
| }
|
| unixLeaveMutex();
|
| @@ -3594,9 +4538,15 @@ int fillInUnixFile(
|
| ** 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, NULL, &pNew->pOpen);
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| + if( rc!=SQLITE_OK ){
|
| + sqlite3_free(pNew->lockingContext);
|
| + robust_close(pNew, h, __LINE__);
|
| + h = -1;
|
| + }
|
| unixLeaveMutex();
|
| }
|
| }
|
| @@ -3624,18 +4574,18 @@ int fillInUnixFile(
|
| ** 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;
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| + if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){
|
| + char *zSemName = pNew->pInode->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 ){
|
| + pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1);
|
| + if( pNew->pInode->pSem == SEM_FAILED ){
|
| rc = SQLITE_NOMEM;
|
| - pNew->pOpen->aSemName[0] = '\0';
|
| + pNew->pInode->aSemName[0] = '\0';
|
| }
|
| }
|
| unixLeaveMutex();
|
| @@ -3645,14 +4595,16 @@ int fillInUnixFile(
|
| pNew->lastErrno = 0;
|
| #if OS_VXWORKS
|
| if( rc!=SQLITE_OK ){
|
| + if( h>=0 ) robust_close(pNew, h, __LINE__);
|
| + 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);
|
| + if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__);
|
| + if( h>=0 ) robust_close(pNew, h, __LINE__);
|
| }else{
|
| pNew->pMethod = pLockingStyle;
|
| OpenCounter(+1);
|
| @@ -3679,39 +4631,59 @@ static int openDirectory(const char *zFilename, int *pFd){
|
| for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
|
| if( ii>0 ){
|
| zDirname[ii] = '\0';
|
| - fd = open(zDirname, O_RDONLY|O_BINARY, 0);
|
| + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
| if( fd>=0 ){
|
| #ifdef FD_CLOEXEC
|
| - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
| + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
| #endif
|
| - OSTRACE3("OPENDIR %-3d %s\n", fd, zDirname);
|
| + OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
| }
|
| }
|
| *pFd = fd;
|
| - return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN);
|
| + return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
|
| }
|
|
|
| /*
|
| -** 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.
|
| +** Return the name of a directory in which to put temporary files.
|
| +** If no suitable temporary file directory can be found, return NULL.
|
| */
|
| -static int getTempname(int nBuf, char *zBuf){
|
| +static const char *unixTempFileDir(void){
|
| static const char *azDirs[] = {
|
| 0,
|
| 0,
|
| "/var/tmp",
|
| "/usr/tmp",
|
| "/tmp",
|
| - ".",
|
| + 0 /* List terminator */
|
| };
|
| + unsigned int i;
|
| + struct stat buf;
|
| + const char *zDir = 0;
|
| +
|
| + azDirs[0] = sqlite3_temp_directory;
|
| + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
| + for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
| + if( zDir==0 ) continue;
|
| + if( osStat(zDir, &buf) ) continue;
|
| + if( !S_ISDIR(buf.st_mode) ) continue;
|
| + if( osAccess(zDir, 07) ) continue;
|
| + break;
|
| + }
|
| + return zDir;
|
| +}
|
| +
|
| +/*
|
| +** 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 unixGetTempname(int nBuf, char *zBuf){
|
| static const unsigned char zChars[] =
|
| "abcdefghijklmnopqrstuvwxyz"
|
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
| "0123456789";
|
| unsigned int i, j;
|
| - struct stat buf;
|
| - const char *zDir = ".";
|
| + const char *zDir;
|
|
|
| /* 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
|
| @@ -3719,19 +4691,8 @@ static int getTempname(int nBuf, char *zBuf){
|
| */
|
| SimulateIOError( return SQLITE_IOERR );
|
|
|
| - azDirs[0] = sqlite3_temp_directory;
|
| - if (NULL == azDirs[1]) {
|
| - azDirs[1] = getenv("TMPDIR");
|
| - }
|
| -
|
| - for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
|
| - if( azDirs[i]==0 ) continue;
|
| - if( stat(azDirs[i], &buf) ) continue;
|
| - if( !S_ISDIR(buf.st_mode) ) continue;
|
| - if( access(azDirs[i], 07) ) continue;
|
| - zDir = azDirs[i];
|
| - break;
|
| - }
|
| + zDir = unixTempFileDir();
|
| + if( zDir==0 ) zDir = ".";
|
|
|
| /* Check that the output buffer is large enough for the temporary file
|
| ** name. If it is not, return SQLITE_ERROR.
|
| @@ -3748,7 +4709,7 @@ static int getTempname(int nBuf, char *zBuf){
|
| zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
| }
|
| zBuf[j] = 0;
|
| - }while( access(zBuf,0)==0 );
|
| + }while( osAccess(zBuf,0)==0 );
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -3797,16 +4758,17 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
| ** 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 *pO;
|
| - struct unixFileId id;
|
| - id.dev = sStat.st_dev;
|
| - id.ino = sStat.st_ino;
|
| + unixInodeInfo *pInode;
|
|
|
| unixEnterMutex();
|
| - for(pO=openList; pO && memcmp(&id, &pO->fileId, sizeof(id)); pO=pO->pNext);
|
| - if( pO ){
|
| + pInode = inodeList;
|
| + while( pInode && (pInode->fileId.dev!=sStat.st_dev
|
| + || pInode->fileId.ino!=sStat.st_ino) ){
|
| + pInode = pInode->pNext;
|
| + }
|
| + if( pInode ){
|
| UnixUnusedFd **pp;
|
| - for(pp=&pO->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
|
| + for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
|
| pUnused = *pp;
|
| if( pUnused ){
|
| *pp = pUnused->pNext;
|
| @@ -3819,6 +4781,66 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
| }
|
|
|
| /*
|
| +** This function is called by unixOpen() to determine the unix permissions
|
| +** to create new files with. If no error occurs, then SQLITE_OK is returned
|
| +** and a value suitable for passing as the third argument to open(2) is
|
| +** written to *pMode. If an IO error occurs, an SQLite error code is
|
| +** returned and the value of *pMode is not modified.
|
| +**
|
| +** If the file being opened is a temporary file, it is always created with
|
| +** the octal permissions 0600 (read/writable by owner only). If the file
|
| +** is a database or master journal file, it is created with the permissions
|
| +** mask SQLITE_DEFAULT_FILE_PERMISSIONS.
|
| +**
|
| +** Finally, if the file being opened is a WAL or regular journal file, then
|
| +** this function queries the file-system for the permissions on the
|
| +** corresponding database file and sets *pMode to this value. Whenever
|
| +** possible, WAL and journal files are created using the same permissions
|
| +** as the associated database file.
|
| +*/
|
| +static int findCreateFileMode(
|
| + const char *zPath, /* Path of file (possibly) being created */
|
| + int flags, /* Flags passed as 4th argument to xOpen() */
|
| + mode_t *pMode /* OUT: Permissions to open file with */
|
| +){
|
| + int rc = SQLITE_OK; /* Return Code */
|
| + if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
| + char zDb[MAX_PATHNAME+1]; /* Database file path */
|
| + int nDb; /* Number of valid bytes in zDb */
|
| + struct stat sStat; /* Output of stat() on database file */
|
| +
|
| + /* zPath is a path to a WAL or journal file. The following block derives
|
| + ** the path to the associated database file from zPath. This block handles
|
| + ** the following naming conventions:
|
| + **
|
| + ** "<path to db>-journal"
|
| + ** "<path to db>-wal"
|
| + ** "<path to db>-journal-NNNN"
|
| + ** "<path to db>-wal-NNNN"
|
| + **
|
| + ** where NNNN is a 4 digit decimal number. The NNNN naming schemes are
|
| + ** used by the test_multiplex.c module.
|
| + */
|
| + nDb = sqlite3Strlen30(zPath) - 1;
|
| + while( nDb>0 && zPath[nDb]!='l' ) nDb--;
|
| + nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7);
|
| + memcpy(zDb, zPath, nDb);
|
| + zDb[nDb] = '\0';
|
| +
|
| + if( 0==stat(zDb, &sStat) ){
|
| + *pMode = sStat.st_mode & 0777;
|
| + }else{
|
| + rc = SQLITE_IOERR_FSTAT;
|
| + }
|
| + }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
| + *pMode = 0600;
|
| + }else{
|
| + *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| ** Initializes a unixFile structure with zeros.
|
| */
|
| void chromium_sqlite3_initialize_unix_sqlite3_file(sqlite3_file* file) {
|
| @@ -3832,7 +4854,7 @@ int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* vfs,
|
| const char* fileName,
|
| int noLock,
|
| int isDelete) {
|
| - return fillInUnixFile(vfs, fd, dirfd, file, fileName, noLock, isDelete);
|
| + return fillInUnixFile(vfs, fd, dirfd, file, fileName, noLock, isDelete, 0);
|
| }
|
|
|
| /*
|
| @@ -3927,14 +4949,19 @@ static int unixOpen(
|
| 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)
|
| - );
|
| + int isOpenDirectory = (isCreate && (
|
| + eType==SQLITE_OPEN_MASTER_JOURNAL
|
| + || eType==SQLITE_OPEN_MAIN_JOURNAL
|
| + || eType==SQLITE_OPEN_WAL
|
| + ));
|
|
|
| /* 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.
|
| @@ -3954,17 +4981,18 @@ static int unixOpen(
|
| 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. */
|
| + /* The main DB, main journal, WAL file 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( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
|
|
|
| /* 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
|
| + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
|
| );
|
|
|
| chromium_sqlite3_initialize_unix_sqlite3_file(pFile);
|
| @@ -3977,7 +5005,7 @@ static int unixOpen(
|
| }else if( !zName ){
|
| /* If zName is NULL, the upper layer is requesting a temp file. */
|
| assert(isDelete && !isOpenDirectory);
|
| - rc = getTempname(MAX_PATHNAME+1, zTmpname);
|
| + rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
|
| if( rc!=SQLITE_OK ){
|
| return rc;
|
| }
|
| @@ -3995,19 +5023,26 @@ static int unixOpen(
|
| 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);
|
| + mode_t openMode; /* Permissions to create file with */
|
| + rc = findCreateFileMode(zName, flags, &openMode);
|
| + if( rc!=SQLITE_OK ){
|
| + assert( !p->pUnused );
|
| + assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
|
| + return rc;
|
| + }
|
| + fd = robust_open(zName, openFlags, openMode);
|
| + OSTRACE(("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);
|
| + isReadonly = 1;
|
| + fd = robust_open(zName, openFlags, openMode);
|
| }
|
| if( fd<0 ){
|
| - rc = SQLITE_CANTOPEN;
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
| goto open_finished;
|
| }
|
| }
|
| @@ -4039,19 +5074,36 @@ static int unixOpen(
|
| ** 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 */
|
| + robust_close(p, fd, __LINE__);
|
| goto open_finished;
|
| }
|
| }
|
|
|
| #ifdef FD_CLOEXEC
|
| - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
| + osFcntl(fd, F_SETFD, osFcntl(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 ) robust_close(p, dirfd, __LINE__);
|
| + robust_close(p, fd, __LINE__);
|
| + 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
|
| - if( zPath!=NULL && !noLock && pVfs->xOpen ){
|
| + isAutoProxy = 1;
|
| +#endif
|
| + if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){
|
| char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
|
| int useProxy = 0;
|
|
|
| @@ -4071,25 +5123,35 @@ static int unixOpen(
|
| ** the same file are working. */
|
| p->lastErrno = errno;
|
| if( dirfd>=0 ){
|
| - close(dirfd); /* silently leak if fail, in error */
|
| + robust_close(p, dirfd, __LINE__);
|
| }
|
| - close(fd); /* silently leak if fail, in error */
|
| + robust_close(p, fd, __LINE__);
|
| rc = SQLITE_IOERR_ACCESS;
|
| goto open_finished;
|
| }
|
| useProxy = !(fsInfo.f_flags&MNT_LOCAL);
|
| }
|
| if( useProxy ){
|
| - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
|
| + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
|
| + isDelete, isReadonly);
|
| 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);
|
| + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
|
| + isDelete, isReadonly);
|
| open_finished:
|
| if( rc!=SQLITE_OK ){
|
| chromium_sqlite3_destroy_reusable_file_handle(pFile);
|
| @@ -4110,7 +5172,9 @@ static int unixDelete(
|
| int rc = SQLITE_OK;
|
| UNUSED_PARAMETER(NotUsed);
|
| SimulateIOError(return SQLITE_IOERR_DELETE);
|
| - unlink(zPath);
|
| + if( unlink(zPath)==(-1) && errno!=ENOENT ){
|
| + return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
| + }
|
| #ifndef SQLITE_DISABLE_DIRSYNC
|
| if( dirSync ){
|
| int fd;
|
| @@ -4122,11 +5186,9 @@ static int unixDelete(
|
| if( fsync(fd) )
|
| #endif
|
| {
|
| - rc = SQLITE_IOERR_DIR_FSYNC;
|
| - }
|
| - if( close(fd)&&!rc ){
|
| - rc = SQLITE_IOERR_DIR_CLOSE;
|
| + rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
|
| }
|
| + robust_close(0, fd, __LINE__);
|
| }
|
| }
|
| #endif
|
| @@ -4166,7 +5228,13 @@ static int unixAccess(
|
| default:
|
| assert(!"Invalid flags argument");
|
| }
|
| - *pResOut = (access(zPath, amode)==0);
|
| + *pResOut = (osAccess(zPath, amode)==0);
|
| + if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
|
| + struct stat buf;
|
| + if( 0==stat(zPath, &buf) && buf.st_size==0 ){
|
| + *pResOut = 0;
|
| + }
|
| + }
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -4202,8 +5270,8 @@ static int unixFullPathname(
|
| sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
| }else{
|
| int nCwd;
|
| - if( getcwd(zOut, nOut-1)==0 ){
|
| - return SQLITE_CANTOPEN;
|
| + if( osGetcwd(zOut, nOut-1)==0 ){
|
| + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
|
| }
|
| nCwd = (int)strlen(zOut);
|
| sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
|
| @@ -4231,7 +5299,7 @@ static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
|
| ** error message.
|
| */
|
| static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){
|
| - char *zErr;
|
| + const char *zErr;
|
| UNUSED_PARAMETER(NotUsed);
|
| unixEnterMutex();
|
| zErr = dlerror();
|
| @@ -4297,7 +5365,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
| #if !defined(SQLITE_TEST)
|
| {
|
| int pid, fd;
|
| - fd = open("/dev/urandom", O_RDONLY);
|
| + fd = robust_open("/dev/urandom", O_RDONLY, 0);
|
| if( fd<0 ){
|
| time_t t;
|
| time(&t);
|
| @@ -4307,8 +5375,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
| assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
|
| nBuf = sizeof(t) + sizeof(pid);
|
| }else{
|
| - nBuf = read(fd, zBuf, nBuf);
|
| - close(fd);
|
| + do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR );
|
| + robust_close(0, fd, __LINE__);
|
| }
|
| }
|
| #endif
|
| @@ -4355,32 +5423,33 @@ int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
| #endif
|
|
|
| /*
|
| -** 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.
|
| +** 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 unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
| -#if defined(SQLITE_OMIT_FLOATING_POINT)
|
| - time_t t;
|
| - time(&t);
|
| - *prNow = (((sqlite3_int64)t)/8640 + 24405875)/10;
|
| -#elif defined(NO_GETTOD)
|
| +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);
|
| - *prNow = t/86400.0 + 2440587.5;
|
| + *piNow = ((sqlite3_int64)t)*1000 + unixEpoch;
|
| #elif OS_VXWORKS
|
| struct timespec sNow;
|
| clock_gettime(CLOCK_REALTIME, &sNow);
|
| - *prNow = 2440587.5 + sNow.tv_sec/86400.0 + sNow.tv_nsec/86400000000000.0;
|
| + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
|
| #else
|
| struct timeval sNow;
|
| gettimeofday(&sNow, 0);
|
| - *prNow = 2440587.5 + sNow.tv_sec/86400.0 + sNow.tv_usec/86400000000.0;
|
| + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
| #endif
|
|
|
| #ifdef SQLITE_TEST
|
| if( sqlite3_current_time ){
|
| - *prNow = sqlite3_current_time/86400.0 + 2440587.5;
|
| + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
|
| }
|
| #endif
|
| UNUSED_PARAMETER(NotUsed);
|
| @@ -4388,6 +5457,19 @@ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
| }
|
|
|
| /*
|
| +** 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;
|
| + UNUSED_PARAMETER(NotUsed);
|
| + 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
|
| @@ -4401,6 +5483,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
| return 0;
|
| }
|
|
|
| +
|
| /*
|
| ************************ End of sqlite3_vfs methods ***************************
|
| ******************************************************************************/
|
| @@ -4510,11 +5593,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
| ** of the database file for multiple readers and writers on the same
|
| ** host (the conch ensures that they all use the same local lock file).
|
| **
|
| -** There is a third file - the host ID file - used as a persistent record
|
| -** of a unique identifier for the host, a 128-byte unique host id file
|
| -** in the path defined by the HOSTIDPATH macro (default value is
|
| -** /Library/Caches/.com.apple.sqliteConchHostId).
|
| -**
|
| ** Requesting the lock proxy does not immediately take the conch, it is
|
| ** only taken when the first request to lock database file is made.
|
| ** This matches the semantics of the traditional locking behavior, where
|
| @@ -4540,10 +5618,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
| ** Enables the logging of error messages during host id file
|
| ** retrieval and creation
|
| **
|
| -** HOSTIDPATH
|
| -**
|
| -** Overrides the default host ID file path location
|
| -**
|
| ** LOCKPROXYDIR
|
| **
|
| ** Overrides the default directory used for lock proxy files that
|
| @@ -4568,11 +5642,6 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
| */
|
| #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
|
|
| -#ifdef SQLITE_TEST
|
| -/* simulate multiple hosts by creating unique hostid file paths */
|
| -int sqlite3_hostid_num = 0;
|
| -#endif
|
| -
|
| /*
|
| ** The proxyLockingContext has the path and file structures for the remote
|
| ** and local proxy files in it
|
| @@ -4584,134 +5653,16 @@ struct proxyLockingContext {
|
| unixFile *lockProxy; /* Open proxy lock file */
|
| char *lockProxyPath; /* Name of the proxy lock file */
|
| char *dbPath; /* Name of the open file */
|
| - int conchHeld; /* True if the conch is currently held */
|
| + int conchHeld; /* 1 if the conch is held, -1 if lockless */
|
| void *oldLockingContext; /* Original lockingcontext to restore on close */
|
| sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */
|
| };
|
|
|
| -/* HOSTIDLEN and CONCHLEN both include space for the string
|
| -** terminating nul
|
| -*/
|
| -#define HOSTIDLEN 128
|
| -#define CONCHLEN (MAXPATHLEN+HOSTIDLEN+1)
|
| -#ifndef HOSTIDPATH
|
| -# define HOSTIDPATH "/Library/Caches/.com.apple.sqliteConchHostId"
|
| -#endif
|
| -
|
| -/* basically a copy of unixRandomness with different
|
| -** test behavior built in */
|
| -static int proxyGenerateHostID(char *pHostID){
|
| - int pid, fd, len;
|
| - unsigned char *key = (unsigned char *)pHostID;
|
| -
|
| - memset(key, 0, HOSTIDLEN);
|
| - len = 0;
|
| - fd = open("/dev/urandom", O_RDONLY);
|
| - if( fd>=0 ){
|
| - len = read(fd, key, HOSTIDLEN);
|
| - close(fd); /* silently leak the fd if it fails */
|
| - }
|
| - if( len < HOSTIDLEN ){
|
| - time_t t;
|
| - time(&t);
|
| - memcpy(key, &t, sizeof(t));
|
| - pid = getpid();
|
| - memcpy(&key[sizeof(t)], &pid, sizeof(pid));
|
| - }
|
| -
|
| -#ifdef MAKE_PRETTY_HOSTID
|
| - {
|
| - int i;
|
| - /* filter the bytes into printable ascii characters and NUL terminate */
|
| - key[(HOSTIDLEN-1)] = 0x00;
|
| - for( i=0; i<(HOSTIDLEN-1); i++ ){
|
| - unsigned char pa = key[i]&0x7F;
|
| - if( pa<0x20 ){
|
| - key[i] = (key[i]&0x80 == 0x80) ? pa+0x40 : pa+0x20;
|
| - }else if( pa==0x7F ){
|
| - key[i] = (key[i]&0x80 == 0x80) ? pa=0x20 : pa+0x7E;
|
| - }
|
| - }
|
| - }
|
| -#endif
|
| - return SQLITE_OK;
|
| -}
|
| -
|
| -/* writes the host id path to path, path should be an pre-allocated buffer
|
| -** with enough space for a path
|
| -*/
|
| -static void proxyGetHostIDPath(char *path, size_t len){
|
| - strlcpy(path, HOSTIDPATH, len);
|
| -#ifdef SQLITE_TEST
|
| - if( sqlite3_hostid_num>0 ){
|
| - char suffix[2] = "1";
|
| - suffix[0] = suffix[0] + sqlite3_hostid_num;
|
| - strlcat(path, suffix, len);
|
| - }
|
| -#endif
|
| - OSTRACE3("GETHOSTIDPATH %s pid=%d\n", path, getpid());
|
| -}
|
| -
|
| -/* get the host ID from a sqlite hostid file stored in the
|
| -** user-specific tmp directory, create the ID if it's not there already
|
| +/*
|
| +** The proxy lock file path for the database at dbPath is written into lPath,
|
| +** which must point to valid, writable memory large enough for a maxLen length
|
| +** file path.
|
| */
|
| -static int proxyGetHostID(char *pHostID, int *pError){
|
| - int fd;
|
| - char path[MAXPATHLEN];
|
| - size_t len;
|
| - int rc=SQLITE_OK;
|
| -
|
| - proxyGetHostIDPath(path, MAXPATHLEN);
|
| - /* try to create the host ID file, if it already exists read the contents */
|
| - fd = open(path, O_CREAT|O_WRONLY|O_EXCL, 0644);
|
| - if( fd<0 ){
|
| - int err=errno;
|
| -
|
| - if( err!=EEXIST ){
|
| -#ifdef SQLITE_PROXY_DEBUG /* set the sqlite error message instead */
|
| - fprintf(stderr, "sqlite error creating host ID file %s: %s\n",
|
| - path, strerror(err));
|
| -#endif
|
| - return SQLITE_PERM;
|
| - }
|
| - /* couldn't create the file, read it instead */
|
| - fd = open(path, O_RDONLY|O_EXCL);
|
| - if( fd<0 ){
|
| -#ifdef SQLITE_PROXY_DEBUG /* set the sqlite error message instead */
|
| - int err = errno;
|
| - fprintf(stderr, "sqlite error opening host ID file %s: %s\n",
|
| - path, strerror(err));
|
| -#endif
|
| - return SQLITE_PERM;
|
| - }
|
| - len = pread(fd, pHostID, HOSTIDLEN, 0);
|
| - if( len<0 ){
|
| - *pError = errno;
|
| - rc = SQLITE_IOERR_READ;
|
| - }else if( len<HOSTIDLEN ){
|
| - *pError = 0;
|
| - rc = SQLITE_IOERR_SHORT_READ;
|
| - }
|
| - close(fd); /* silently leak the fd if it fails */
|
| - OSTRACE3("GETHOSTID read %s pid=%d\n", pHostID, getpid());
|
| - return rc;
|
| - }else{
|
| - /* we're creating the host ID file (use a random string of bytes) */
|
| - proxyGenerateHostID(pHostID);
|
| - len = pwrite(fd, pHostID, HOSTIDLEN, 0);
|
| - if( len<0 ){
|
| - *pError = errno;
|
| - rc = SQLITE_IOERR_WRITE;
|
| - }else if( len<HOSTIDLEN ){
|
| - *pError = 0;
|
| - rc = SQLITE_IOERR_WRITE;
|
| - }
|
| - close(fd); /* silently leak the fd if it fails */
|
| - OSTRACE3("GETHOSTID wrote %s pid=%d\n", pHostID, getpid());
|
| - return rc;
|
| - }
|
| -}
|
| -
|
| static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
|
| int len;
|
| int dbLen;
|
| @@ -4722,21 +5673,12 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
|
| #else
|
| # ifdef _CS_DARWIN_USER_TEMP_DIR
|
| {
|
| - confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen);
|
| - len = strlcat(lPath, "sqliteplocks", maxLen);
|
| - if( mkdir(lPath, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
|
| - /* if mkdir fails, handle as lock file creation failure */
|
| -# ifdef SQLITE_DEBUG
|
| - int err = errno;
|
| - if( err!=EEXIST ){
|
| - fprintf(stderr, "proxyGetLockPath: mkdir(%s,0%o) error %d %s\n", lPath,
|
| - SQLITE_DEFAULT_PROXYDIR_PERMISSIONS, err, strerror(err));
|
| - }
|
| -# endif
|
| - }else{
|
| - OSTRACE3("GETLOCKPATH mkdir %s pid=%d\n", lPath, getpid());
|
| + if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){
|
| + OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n",
|
| + lPath, errno, getpid()));
|
| + return SQLITE_IOERR_LOCK;
|
| }
|
| -
|
| + len = strlcat(lPath, "sqliteplocks", maxLen);
|
| }
|
| # else
|
| len = strlcpy(lPath, "/tmp/", maxLen);
|
| @@ -4749,15 +5691,52 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
|
|
|
| /* transform the db path to a unique cache name */
|
| dbLen = (int)strlen(dbPath);
|
| - for( i=0; i<dbLen && (i+len+7)<maxLen; i++){
|
| + for( i=0; i<dbLen && (i+len+7)<(int)maxLen; i++){
|
| char c = dbPath[i];
|
| lPath[i+len] = (c=='/')?'_':c;
|
| }
|
| lPath[i+len]='\0';
|
| strlcat(lPath, ":auto:", maxLen);
|
| + OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, getpid()));
|
| return SQLITE_OK;
|
| }
|
|
|
| +/*
|
| + ** Creates the lock file and any missing directories in lockPath
|
| + */
|
| +static int proxyCreateLockPath(const char *lockPath){
|
| + int i, len;
|
| + char buf[MAXPATHLEN];
|
| + int start = 0;
|
| +
|
| + assert(lockPath!=NULL);
|
| + /* try to create all the intermediate directories */
|
| + len = (int)strlen(lockPath);
|
| + buf[0] = lockPath[0];
|
| + for( i=1; i<len; i++ ){
|
| + if( lockPath[i] == '/' && (i - start > 0) ){
|
| + /* only mkdir if leaf dir != "." or "/" or ".." */
|
| + if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|
| + || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
|
| + buf[i]='\0';
|
| + if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
|
| + int err=errno;
|
| + if( err!=EEXIST ) {
|
| + OSTRACE(("CREATELOCKPATH FAILED creating %s, "
|
| + "'%s' proxy lock path=%s pid=%d\n",
|
| + buf, strerror(err), lockPath, getpid()));
|
| + return err;
|
| + }
|
| + }
|
| + }
|
| + start=i+1;
|
| + }
|
| + buf[i] = lockPath[i];
|
| + }
|
| + OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid()));
|
| + return 0;
|
| +}
|
| +
|
| /*
|
| ** Create a new VFS file descriptor (stored in memory obtained from
|
| ** sqlite3_malloc) and open the file named "path" in the file descriptor.
|
| @@ -4765,48 +5744,274 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
|
| ** The caller is responsible not only for closing the file descriptor
|
| ** but also for freeing the memory associated with the file descriptor.
|
| */
|
| -static int proxyCreateUnixFile(const char *path, unixFile **ppFile) {
|
| +static int proxyCreateUnixFile(
|
| + const char *path, /* path for the new unixFile */
|
| + unixFile **ppFile, /* unixFile created and returned by ref */
|
| + int islockfile /* if non zero missing dirs will be created */
|
| +) {
|
| + int fd = -1;
|
| + int dirfd = -1;
|
| unixFile *pNew;
|
| - int flags = SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
|
| int rc = SQLITE_OK;
|
| + int openFlags = O_RDWR | O_CREAT;
|
| sqlite3_vfs dummyVfs;
|
| -
|
| - pNew = (unixFile *)sqlite3_malloc(sizeof(unixFile));
|
| - if( !pNew ){
|
| - return SQLITE_NOMEM;
|
| + int terrno = 0;
|
| + UnixUnusedFd *pUnused = NULL;
|
| +
|
| + /* 1. first try to open/create the file
|
| + ** 2. if that fails, and this is a lock file (not-conch), try creating
|
| + ** the parent directories and then try again.
|
| + ** 3. if that fails, try to open the file read-only
|
| + ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file
|
| + */
|
| + pUnused = findReusableFd(path, openFlags);
|
| + if( pUnused ){
|
| + fd = pUnused->fd;
|
| + }else{
|
| + pUnused = sqlite3_malloc(sizeof(*pUnused));
|
| + if( !pUnused ){
|
| + return SQLITE_NOMEM;
|
| + }
|
| + }
|
| + if( fd<0 ){
|
| + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| + terrno = errno;
|
| + if( fd<0 && errno==ENOENT && islockfile ){
|
| + if( proxyCreateLockPath(path) == SQLITE_OK ){
|
| + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| + }
|
| + }
|
| + }
|
| + if( fd<0 ){
|
| + openFlags = O_RDONLY;
|
| + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| + terrno = errno;
|
| + }
|
| + if( fd<0 ){
|
| + if( islockfile ){
|
| + return SQLITE_BUSY;
|
| + }
|
| + switch (terrno) {
|
| + case EACCES:
|
| + return SQLITE_PERM;
|
| + case EIO:
|
| + return SQLITE_IOERR_LOCK; /* even though it is the conch */
|
| + default:
|
| + return SQLITE_CANTOPEN_BKPT;
|
| + }
|
| + }
|
| +
|
| + pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew));
|
| + if( pNew==NULL ){
|
| + rc = SQLITE_NOMEM;
|
| + goto end_create_proxy;
|
| }
|
| memset(pNew, 0, sizeof(unixFile));
|
| -
|
| - /* Call unixOpen() to open the proxy file. The flags passed to unixOpen()
|
| - ** suggest that the file being opened is a "main database". This is
|
| - ** necessary as other file types do not necessarily support locking. It
|
| - ** is better to use unixOpen() instead of opening the file directly with
|
| - ** open(), as unixOpen() sets up the various mechanisms required to
|
| - ** make sure a call to close() does not cause the system to discard
|
| - ** POSIX locks prematurely.
|
| - **
|
| - ** It is important that the xOpen member of the VFS object passed to
|
| - ** unixOpen() is NULL. This tells unixOpen() may try to open a proxy-file
|
| - ** for the proxy-file (creating a potential infinite loop).
|
| - */
|
| + pNew->openFlags = openFlags;
|
| + memset(&dummyVfs, 0, sizeof(dummyVfs));
|
| dummyVfs.pAppData = (void*)&autolockIoFinder;
|
| - dummyVfs.xOpen = 0;
|
| - rc = unixOpen(&dummyVfs, path, (sqlite3_file *)pNew, flags, &flags);
|
| - if( rc==SQLITE_OK && (flags&SQLITE_OPEN_READONLY) ){
|
| - pNew->pMethod->xClose((sqlite3_file *)pNew);
|
| - rc = SQLITE_CANTOPEN;
|
| + dummyVfs.zName = "dummy";
|
| + pUnused->fd = fd;
|
| + pUnused->flags = openFlags;
|
| + pNew->pUnused = pUnused;
|
| +
|
| + rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0);
|
| + if( rc==SQLITE_OK ){
|
| + *ppFile = pNew;
|
| + return SQLITE_OK;
|
| }
|
| +end_create_proxy:
|
| + robust_close(pNew, fd, __LINE__);
|
| + sqlite3_free(pNew);
|
| + sqlite3_free(pUnused);
|
| + return rc;
|
| +}
|
|
|
| - if( rc!=SQLITE_OK ){
|
| - sqlite3_free(pNew);
|
| - pNew = 0;
|
| +#ifdef SQLITE_TEST
|
| +/* simulate multiple hosts by creating unique hostid file paths */
|
| +int sqlite3_hostid_num = 0;
|
| +#endif
|
| +
|
| +#define PROXY_HOSTIDLEN 16 /* conch file host id length */
|
| +
|
| +/* Not always defined in the headers as it ought to be */
|
| +extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
| +
|
| +/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
|
| +** bytes of writable memory.
|
| +*/
|
| +static int proxyGetHostID(unsigned char *pHostID, int *pError){
|
| + assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
|
| + memset(pHostID, 0, PROXY_HOSTIDLEN);
|
| +#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\
|
| + && __MAC_OS_X_VERSION_MIN_REQUIRED<1050
|
| + {
|
| + static const struct timespec timeout = {1, 0}; /* 1 sec timeout */
|
| + if( gethostuuid(pHostID, &timeout) ){
|
| + int err = errno;
|
| + if( pError ){
|
| + *pError = err;
|
| + }
|
| + return SQLITE_IOERR;
|
| + }
|
| + }
|
| +#endif
|
| +#ifdef SQLITE_TEST
|
| + /* simulate multiple hosts by creating unique hostid file paths */
|
| + if( sqlite3_hostid_num != 0){
|
| + pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF));
|
| + }
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/* The conch file contains the header, host id and lock file path
|
| + */
|
| +#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */
|
| +#define PROXY_HEADERLEN 1 /* conch file header length */
|
| +#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN)
|
| +#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN)
|
| +
|
| +/*
|
| +** Takes an open conch file, copies the contents to a new path and then moves
|
| +** it back. The newly created file's file descriptor is assigned to the
|
| +** conch file structure and finally the original conch file descriptor is
|
| +** closed. Returns zero if successful.
|
| +*/
|
| +static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + char tPath[MAXPATHLEN];
|
| + char buf[PROXY_MAXCONCHLEN];
|
| + char *cPath = pCtx->conchFilePath;
|
| + size_t readLen = 0;
|
| + size_t pathLen = 0;
|
| + char errmsg[64] = "";
|
| + int fd = -1;
|
| + int rc = -1;
|
| + UNUSED_PARAMETER(myHostID);
|
| +
|
| + /* create a new path by replace the trailing '-conch' with '-break' */
|
| + pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
|
| + if( pathLen>MAXPATHLEN || pathLen<6 ||
|
| + (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
|
| + sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen);
|
| + goto end_breaklock;
|
| + }
|
| + /* read the conch content */
|
| + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0);
|
| + if( readLen<PROXY_PATHINDEX ){
|
| + sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen);
|
| + goto end_breaklock;
|
| + }
|
| + /* write it out to the temporary break file */
|
| + fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL),
|
| + SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| + if( fd<0 ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + if( osPwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + if( rename(tPath, cPath) ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + rc = 0;
|
| + fprintf(stderr, "broke stale lock on %s\n", cPath);
|
| + robust_close(pFile, conchFile->h, __LINE__);
|
| + conchFile->h = fd;
|
| + conchFile->openFlags = O_RDWR | O_CREAT;
|
| +
|
| +end_breaklock:
|
| + if( rc ){
|
| + if( fd>=0 ){
|
| + unlink(tPath);
|
| + robust_close(pFile, fd, __LINE__);
|
| + }
|
| + fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
|
| }
|
| + return rc;
|
| +}
|
| +
|
| +/* Take the requested lock on the conch file and break a stale lock if the
|
| +** host id matches.
|
| +*/
|
| +static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + int rc = SQLITE_OK;
|
| + int nTries = 0;
|
| + struct timespec conchModTime;
|
| +
|
| + do {
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
|
| + nTries ++;
|
| + if( rc==SQLITE_BUSY ){
|
| + /* If the lock failed (busy):
|
| + * 1st try: get the mod time of the conch, wait 0.5s and try again.
|
| + * 2nd try: fail if the mod time changed or host id is different, wait
|
| + * 10 sec and try again
|
| + * 3rd try: break the lock unless the mod time has changed.
|
| + */
|
| + struct stat buf;
|
| + if( osFstat(conchFile->h, &buf) ){
|
| + pFile->lastErrno = errno;
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| +
|
| + if( nTries==1 ){
|
| + conchModTime = buf.st_mtimespec;
|
| + usleep(500000); /* wait 0.5 sec and try the lock again*/
|
| + continue;
|
| + }
|
|
|
| - *ppFile = pNew;
|
| + assert( nTries>1 );
|
| + if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec ||
|
| + conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){
|
| + return SQLITE_BUSY;
|
| + }
|
| +
|
| + if( nTries==2 ){
|
| + char tBuf[PROXY_MAXCONCHLEN];
|
| + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0);
|
| + if( len<0 ){
|
| + pFile->lastErrno = errno;
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| + if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){
|
| + /* don't break the lock if the host id doesn't match */
|
| + if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){
|
| + return SQLITE_BUSY;
|
| + }
|
| + }else{
|
| + /* don't break the lock on short read or a version mismatch */
|
| + return SQLITE_BUSY;
|
| + }
|
| + usleep(10000000); /* wait 10 sec and try the lock again */
|
| + continue;
|
| + }
|
| +
|
| + assert( nTries==3 );
|
| + if( 0==proxyBreakConchLock(pFile, myHostID) ){
|
| + rc = SQLITE_OK;
|
| + if( lockType==EXCLUSIVE_LOCK ){
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| + }
|
| + if( !rc ){
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
|
| + }
|
| + }
|
| + }
|
| + } while( rc==SQLITE_BUSY && nTries<3 );
|
| +
|
| return rc;
|
| }
|
|
|
| -/* takes the conch by taking a shared lock and read the contents conch, if
|
| +/* Takes the conch by taking a shared lock and read the contents conch, if
|
| ** lockPath is non-NULL, the host ID and lock file path must match. A NULL
|
| ** lockPath means that the lockPath in the conch file will be used if the
|
| ** host IDs match, or a new lock path will be generated automatically
|
| @@ -4815,149 +6020,217 @@ static int proxyCreateUnixFile(const char *path, unixFile **ppFile) {
|
| static int proxyTakeConch(unixFile *pFile){
|
| proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
|
|
| - if( pCtx->conchHeld>0 ){
|
| + if( pCtx->conchHeld!=0 ){
|
| return SQLITE_OK;
|
| }else{
|
| unixFile *conchFile = pCtx->conchFile;
|
| - char testValue[CONCHLEN];
|
| - char conchValue[CONCHLEN];
|
| + uuid_t myHostID;
|
| + int pError = 0;
|
| + char readBuf[PROXY_MAXCONCHLEN];
|
| char lockPath[MAXPATHLEN];
|
| - char *tLockPath = NULL;
|
| + char *tempLockPath = NULL;
|
| int rc = SQLITE_OK;
|
| - int readRc = SQLITE_OK;
|
| - int syncPerms = 0;
|
| -
|
| - OSTRACE4("TAKECONCH %d for %s pid=%d\n", conchFile->h,
|
| - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid());
|
| + int createConch = 0;
|
| + int hostIdMatch = 0;
|
| + int readLen = 0;
|
| + int tryOldLockPath = 0;
|
| + int forceNewLockPath = 0;
|
| +
|
| + OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h,
|
| + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid()));
|
|
|
| - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| - if( rc==SQLITE_OK ){
|
| - int pError = 0;
|
| - memset(testValue, 0, CONCHLEN); /* conch is fixed size */
|
| - rc = proxyGetHostID(testValue, &pError);
|
| - if( (rc&0xff)==SQLITE_IOERR ){
|
| - pFile->lastErrno = pError;
|
| - }
|
| - if( pCtx->lockProxyPath ){
|
| - strlcpy(&testValue[HOSTIDLEN], pCtx->lockProxyPath, MAXPATHLEN);
|
| - }
|
| + rc = proxyGetHostID(myHostID, &pError);
|
| + if( (rc&0xff)==SQLITE_IOERR ){
|
| + pFile->lastErrno = pError;
|
| + goto end_takeconch;
|
| }
|
| + rc = proxyConchLock(pFile, myHostID, SHARED_LOCK);
|
| if( rc!=SQLITE_OK ){
|
| goto end_takeconch;
|
| }
|
| -
|
| - readRc = unixRead((sqlite3_file *)conchFile, conchValue, CONCHLEN, 0);
|
| - if( readRc!=SQLITE_IOERR_SHORT_READ ){
|
| - if( readRc!=SQLITE_OK ){
|
| - if( (rc&0xff)==SQLITE_IOERR ){
|
| - pFile->lastErrno = conchFile->lastErrno;
|
| + /* read the existing conch file */
|
| + readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN);
|
| + if( readLen<0 ){
|
| + /* I/O error: lastErrno set by seekAndRead */
|
| + pFile->lastErrno = conchFile->lastErrno;
|
| + rc = SQLITE_IOERR_READ;
|
| + goto end_takeconch;
|
| + }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) ||
|
| + readBuf[0]!=(char)PROXY_CONCHVERSION ){
|
| + /* a short read or version format mismatch means we need to create a new
|
| + ** conch file.
|
| + */
|
| + createConch = 1;
|
| + }
|
| + /* if the host id matches and the lock path already exists in the conch
|
| + ** we'll try to use the path there, if we can't open that path, we'll
|
| + ** retry with a new auto-generated path
|
| + */
|
| + do { /* in case we need to try again for an :auto: named lock file */
|
| +
|
| + if( !createConch && !forceNewLockPath ){
|
| + hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID,
|
| + PROXY_HOSTIDLEN);
|
| + /* if the conch has data compare the contents */
|
| + if( !pCtx->lockProxyPath ){
|
| + /* for auto-named local lock file, just check the host ID and we'll
|
| + ** use the local lock file path that's already in there
|
| + */
|
| + if( hostIdMatch ){
|
| + size_t pathLen = (readLen - PROXY_PATHINDEX);
|
| +
|
| + if( pathLen>=MAXPATHLEN ){
|
| + pathLen=MAXPATHLEN-1;
|
| + }
|
| + memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen);
|
| + lockPath[pathLen] = 0;
|
| + tempLockPath = lockPath;
|
| + tryOldLockPath = 1;
|
| + /* create a copy of the lock path if the conch is taken */
|
| + goto end_takeconch;
|
| + }
|
| + }else if( hostIdMatch
|
| + && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX],
|
| + readLen-PROXY_PATHINDEX)
|
| + ){
|
| + /* conch host and lock path match */
|
| + goto end_takeconch;
|
| }
|
| - rc = readRc;
|
| + }
|
| +
|
| + /* if the conch isn't writable and doesn't match, we can't take it */
|
| + if( (conchFile->openFlags&O_RDWR) == 0 ){
|
| + rc = SQLITE_BUSY;
|
| goto end_takeconch;
|
| }
|
| - /* if the conch has data compare the contents */
|
| +
|
| + /* either the conch didn't match or we need to create a new one */
|
| if( !pCtx->lockProxyPath ){
|
| - /* for auto-named local lock file, just check the host ID and we'll
|
| - ** use the local lock file path that's already in there */
|
| - if( !memcmp(testValue, conchValue, HOSTIDLEN) ){
|
| - tLockPath = (char *)&conchValue[HOSTIDLEN];
|
| - goto end_takeconch;
|
| + proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
|
| + tempLockPath = lockPath;
|
| + /* create a copy of the lock path _only_ if the conch is taken */
|
| + }
|
| +
|
| + /* update conch with host and path (this will fail if other process
|
| + ** has a shared lock already), if the host id matches, use the big
|
| + ** stick.
|
| + */
|
| + futimes(conchFile->h, NULL);
|
| + if( hostIdMatch && !createConch ){
|
| + if( conchFile->pInode && conchFile->pInode->nShared>1 ){
|
| + /* We are trying for an exclusive lock but another thread in this
|
| + ** same process is still holding a shared lock. */
|
| + rc = SQLITE_BUSY;
|
| + } else {
|
| + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
|
| }
|
| }else{
|
| - /* we've got the conch if conchValue matches our path and host ID */
|
| - if( !memcmp(testValue, conchValue, CONCHLEN) ){
|
| - goto end_takeconch;
|
| - }
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK);
|
| }
|
| - }else{
|
| - /* a short read means we're "creating" the conch (even though it could
|
| - ** have been user-intervention), if we acquire the exclusive lock,
|
| - ** we'll try to match the current on-disk permissions of the database
|
| - */
|
| - syncPerms = 1;
|
| - }
|
| -
|
| - /* either conch was emtpy or didn't match */
|
| - if( !pCtx->lockProxyPath ){
|
| - proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
|
| - tLockPath = lockPath;
|
| - strlcpy(&testValue[HOSTIDLEN], lockPath, MAXPATHLEN);
|
| - }
|
| -
|
| - /* update conch with host and path (this will fail if other process
|
| - ** has a shared lock already) */
|
| - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK);
|
| - if( rc==SQLITE_OK ){
|
| - rc = unixWrite((sqlite3_file *)conchFile, testValue, CONCHLEN, 0);
|
| - if( rc==SQLITE_OK && syncPerms ){
|
| - struct stat buf;
|
| - int err = fstat(pFile->h, &buf);
|
| - if( err==0 ){
|
| - /* try to match the database file permissions, ignore failure */
|
| + if( rc==SQLITE_OK ){
|
| + char writeBuffer[PROXY_MAXCONCHLEN];
|
| + int writeSize = 0;
|
| +
|
| + writeBuffer[0] = (char)PROXY_CONCHVERSION;
|
| + memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN);
|
| + if( pCtx->lockProxyPath!=NULL ){
|
| + strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN);
|
| + }else{
|
| + strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN);
|
| + }
|
| + writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
|
| + robust_ftruncate(conchFile->h, writeSize);
|
| + rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
|
| + fsync(conchFile->h);
|
| + /* If we created a new conch file (not just updated the contents of a
|
| + ** valid conch file), try to match the permissions of the database
|
| + */
|
| + if( rc==SQLITE_OK && createConch ){
|
| + struct stat buf;
|
| + int err = osFstat(pFile->h, &buf);
|
| + if( err==0 ){
|
| + mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP |
|
| + S_IROTH|S_IWOTH);
|
| + /* try to match the database file R/W permissions, ignore failure */
|
| #ifndef SQLITE_PROXY_DEBUG
|
| - fchmod(conchFile->h, buf.st_mode);
|
| + osFchmod(conchFile->h, cmode);
|
| #else
|
| - if( fchmod(conchFile->h, buf.st_mode)!=0 ){
|
| + do{
|
| + rc = osFchmod(conchFile->h, cmode);
|
| + }while( rc==(-1) && errno==EINTR );
|
| + if( rc!=0 ){
|
| + int code = errno;
|
| + fprintf(stderr, "fchmod %o FAILED with %d %s\n",
|
| + cmode, code, strerror(code));
|
| + } else {
|
| + fprintf(stderr, "fchmod %o SUCCEDED\n",cmode);
|
| + }
|
| + }else{
|
| int code = errno;
|
| - fprintf(stderr, "fchmod %o FAILED with %d %s\n",
|
| - buf.st_mode, code, strerror(code));
|
| - } else {
|
| - fprintf(stderr, "fchmod %o SUCCEDED\n",buf.st_mode);
|
| + fprintf(stderr, "STAT FAILED[%d] with %d %s\n",
|
| + err, code, strerror(code));
|
| +#endif
|
| }
|
| + }
|
| + }
|
| + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| +
|
| + end_takeconch:
|
| + OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
|
| + if( rc==SQLITE_OK && pFile->openFlags ){
|
| + if( pFile->h>=0 ){
|
| + robust_close(pFile, pFile->h, __LINE__);
|
| + }
|
| + pFile->h = -1;
|
| + int fd = robust_open(pCtx->dbPath, pFile->openFlags,
|
| + SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| + OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
|
| + if( fd>=0 ){
|
| + pFile->h = fd;
|
| }else{
|
| - int code = errno;
|
| - fprintf(stderr, "STAT FAILED[%d] with %d %s\n",
|
| - err, code, strerror(code));
|
| -#endif
|
| + rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called
|
| + during locking */
|
| }
|
| }
|
| - }
|
| - conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| -
|
| -end_takeconch:
|
| - OSTRACE2("TRANSPROXY: CLOSE %d\n", pFile->h);
|
| - if( rc==SQLITE_OK && pFile->openFlags ){
|
| - if( pFile->h>=0 ){
|
| -#ifdef STRICT_CLOSE_ERROR
|
| - if( close(pFile->h) ){
|
| - pFile->lastErrno = errno;
|
| - return SQLITE_IOERR_CLOSE;
|
| + if( rc==SQLITE_OK && !pCtx->lockProxy ){
|
| + char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath;
|
| + rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1);
|
| + if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){
|
| + /* we couldn't create the proxy lock file with the old lock file path
|
| + ** so try again via auto-naming
|
| + */
|
| + forceNewLockPath = 1;
|
| + tryOldLockPath = 0;
|
| + continue; /* go back to the do {} while start point, try again */
|
| }
|
| -#else
|
| - close(pFile->h); /* silently leak fd if fail */
|
| -#endif
|
| }
|
| - pFile->h = -1;
|
| - int fd = open(pCtx->dbPath, pFile->openFlags,
|
| - SQLITE_DEFAULT_FILE_PERMISSIONS);
|
| - OSTRACE2("TRANSPROXY: OPEN %d\n", fd);
|
| - if( fd>=0 ){
|
| - pFile->h = fd;
|
| - }else{
|
| - rc=SQLITE_CANTOPEN; /* SQLITE_BUSY? proxyTakeConch called
|
| - during locking */
|
| + if( rc==SQLITE_OK ){
|
| + /* Need to make a copy of path if we extracted the value
|
| + ** from the conch file or the path was allocated on the stack
|
| + */
|
| + if( tempLockPath ){
|
| + pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
|
| + if( !pCtx->lockProxyPath ){
|
| + rc = SQLITE_NOMEM;
|
| + }
|
| + }
|
| }
|
| - }
|
| - if( rc==SQLITE_OK && !pCtx->lockProxy ){
|
| - char *path = tLockPath ? tLockPath : pCtx->lockProxyPath;
|
| - /* ACS: Need to make a copy of path sometimes */
|
| - rc = proxyCreateUnixFile(path, &pCtx->lockProxy);
|
| - }
|
| - if( rc==SQLITE_OK ){
|
| - pCtx->conchHeld = 1;
|
| -
|
| - if( tLockPath ){
|
| - pCtx->lockProxyPath = sqlite3DbStrDup(0, tLockPath);
|
| + if( rc==SQLITE_OK ){
|
| + pCtx->conchHeld = 1;
|
| +
|
| if( pCtx->lockProxy->pMethod == &afpIoMethods ){
|
| - ((afpLockingContext *)pCtx->lockProxy->lockingContext)->dbPath =
|
| - pCtx->lockProxyPath;
|
| + afpLockingContext *afpCtx;
|
| + afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext;
|
| + afpCtx->dbPath = pCtx->lockProxyPath;
|
| }
|
| + } else {
|
| + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| }
|
| - } else {
|
| - conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| - }
|
| - OSTRACE3("TAKECONCH %d %s\n", conchFile->h, rc==SQLITE_OK?"ok":"failed");
|
| - return rc;
|
| + OSTRACE(("TAKECONCH %d %s\n", conchFile->h,
|
| + rc==SQLITE_OK?"ok":"failed"));
|
| + return rc;
|
| + } while (1); /* in case we need to retry the :auto: lock file -
|
| + ** we should never get here except via the 'continue' call. */
|
| }
|
| }
|
|
|
| @@ -4965,19 +6238,21 @@ end_takeconch:
|
| ** If pFile holds a lock on a conch file, then release that lock.
|
| */
|
| static int proxyReleaseConch(unixFile *pFile){
|
| - int rc; /* Subroutine return code */
|
| + int rc = SQLITE_OK; /* Subroutine return code */
|
| proxyLockingContext *pCtx; /* The locking context for the proxy lock */
|
| unixFile *conchFile; /* Name of the conch file */
|
|
|
| pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| conchFile = pCtx->conchFile;
|
| - OSTRACE4("RELEASECONCH %d for %s pid=%d\n", conchFile->h,
|
| + OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h,
|
| (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
|
| - getpid());
|
| + getpid()));
|
| + if( pCtx->conchHeld>0 ){
|
| + rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| + }
|
| pCtx->conchHeld = 0;
|
| - rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| - OSTRACE3("RELEASECONCH %d %s\n", conchFile->h,
|
| - (rc==SQLITE_OK ? "ok" : "failed"));
|
| + OSTRACE(("RELEASECONCH %d %s\n", conchFile->h,
|
| + (rc==SQLITE_OK ? "ok" : "failed")));
|
| return rc;
|
| }
|
|
|
| @@ -5034,7 +6309,7 @@ static int switchLockProxyPath(unixFile *pFile, const char *path) {
|
| char *oldPath = pCtx->lockProxyPath;
|
| int rc = SQLITE_OK;
|
|
|
| - if( pFile->locktype!=NO_LOCK ){
|
| + if( pFile->eFileLock!=NO_LOCK ){
|
| return SQLITE_BUSY;
|
| }
|
|
|
| @@ -5071,8 +6346,8 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
|
| /* afp style keeps a reference to the db path in the filePath field
|
| ** of the struct */
|
| assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
|
| - strcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath);
|
| - }else
|
| + strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN);
|
| + } else
|
| #endif
|
| if( pFile->pMethod == &dotlockIoMethods ){
|
| /* dot lock style uses the locking context to store the dot lock
|
| @@ -5082,7 +6357,7 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
|
| }else{
|
| /* all other styles use the locking context to store the db file path */
|
| assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
|
| - strcpy(dbPath, (char *)pFile->lockingContext);
|
| + strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN);
|
| }
|
| return SQLITE_OK;
|
| }
|
| @@ -5101,7 +6376,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
| char *lockPath=NULL;
|
| int rc = SQLITE_OK;
|
|
|
| - if( pFile->locktype!=NO_LOCK ){
|
| + if( pFile->eFileLock!=NO_LOCK ){
|
| return SQLITE_BUSY;
|
| }
|
| proxyGetDbPathForUnixFile(pFile, dbPath);
|
| @@ -5111,8 +6386,8 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
| lockPath=(char *)path;
|
| }
|
|
|
| - OSTRACE4("TRANSPROXY %d for %s pid=%d\n", pFile->h,
|
| - (lockPath ? lockPath : ":auto:"), getpid());
|
| + OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h,
|
| + (lockPath ? lockPath : ":auto:"), getpid()));
|
|
|
| pCtx = sqlite3_malloc( sizeof(*pCtx) );
|
| if( pCtx==0 ){
|
| @@ -5122,32 +6397,58 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
|
|
| rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath);
|
| if( rc==SQLITE_OK ){
|
| - rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile);
|
| + rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0);
|
| + if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){
|
| + /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and
|
| + ** (c) the file system is read-only, then enable no-locking access.
|
| + ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts
|
| + ** that openFlags will have only one of O_RDONLY or O_RDWR.
|
| + */
|
| + struct statfs fsInfo;
|
| + struct stat conchInfo;
|
| + int goLockless = 0;
|
| +
|
| + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) {
|
| + int err = errno;
|
| + if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){
|
| + goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY;
|
| + }
|
| + }
|
| + if( goLockless ){
|
| + pCtx->conchHeld = -1; /* read only FS/ lockless */
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| }
|
| if( rc==SQLITE_OK && lockPath ){
|
| pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath);
|
| }
|
|
|
| if( rc==SQLITE_OK ){
|
| + pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
|
| + if( pCtx->dbPath==NULL ){
|
| + rc = SQLITE_NOMEM;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| /* all memory is allocated, proxys are created and assigned,
|
| ** switch the locking context and pMethod then return.
|
| */
|
| - pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
|
| pCtx->oldLockingContext = pFile->lockingContext;
|
| pFile->lockingContext = pCtx;
|
| pCtx->pOldMethod = pFile->pMethod;
|
| pFile->pMethod = &proxyIoMethods;
|
| }else{
|
| if( pCtx->conchFile ){
|
| - rc = pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
|
| - if( rc ) return rc;
|
| + pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
|
| sqlite3_free(pCtx->conchFile);
|
| }
|
| + sqlite3DbFree(0, pCtx->lockProxyPath);
|
| sqlite3_free(pCtx->conchFilePath);
|
| sqlite3_free(pCtx);
|
| }
|
| - OSTRACE3("TRANSPROXY %d %s\n", pFile->h,
|
| - (rc==SQLITE_OK ? "ok" : "failed"));
|
| + OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
|
| + (rc==SQLITE_OK ? "ok" : "failed")));
|
| return rc;
|
| }
|
|
|
| @@ -5231,14 +6532,18 @@ static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| int rc = proxyTakeConch(pFile);
|
| if( rc==SQLITE_OK ){
|
| proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| - unixFile *proxy = pCtx->lockProxy;
|
| - return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut);
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut);
|
| + }else{ /* conchHeld < 0 is lockless */
|
| + pResOut=0;
|
| + }
|
| }
|
| return rc;
|
| }
|
|
|
| /*
|
| -** Lock the file with the lock specified by parameter locktype - one
|
| +** Lock the file with the lock specified by parameter eFileLock - one
|
| ** of the following:
|
| **
|
| ** (1) SHARED_LOCK
|
| @@ -5261,34 +6566,42 @@ static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| ** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| ** routine to lower a locking level.
|
| */
|
| -static int proxyLock(sqlite3_file *id, int locktype) {
|
| +static int proxyLock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| int rc = proxyTakeConch(pFile);
|
| if( rc==SQLITE_OK ){
|
| proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| - unixFile *proxy = pCtx->lockProxy;
|
| - rc = proxy->pMethod->xLock((sqlite3_file*)proxy, locktype);
|
| - pFile->locktype = proxy->locktype;
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + rc = proxy->pMethod->xLock((sqlite3_file*)proxy, eFileLock);
|
| + pFile->eFileLock = proxy->eFileLock;
|
| + }else{
|
| + /* conchHeld < 0 is lockless */
|
| + }
|
| }
|
| return rc;
|
| }
|
|
|
|
|
| /*
|
| -** Lower the locking level on file descriptor pFile to locktype. locktype
|
| +** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
|
| ** must be either NO_LOCK or SHARED_LOCK.
|
| **
|
| ** If the locking level of the file descriptor is already at or below
|
| ** the requested locking level, this routine is a no-op.
|
| */
|
| -static int proxyUnlock(sqlite3_file *id, int locktype) {
|
| +static int proxyUnlock(sqlite3_file *id, int eFileLock) {
|
| unixFile *pFile = (unixFile*)id;
|
| int rc = proxyTakeConch(pFile);
|
| if( rc==SQLITE_OK ){
|
| proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| - unixFile *proxy = pCtx->lockProxy;
|
| - rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, locktype);
|
| - pFile->locktype = proxy->locktype;
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, eFileLock);
|
| + pFile->eFileLock = proxy->eFileLock;
|
| + }else{
|
| + /* conchHeld < 0 is lockless */
|
| + }
|
| }
|
| return rc;
|
| }
|
| @@ -5321,9 +6634,9 @@ static int proxyClose(sqlite3_file *id) {
|
| if( rc ) return rc;
|
| sqlite3_free(conchFile);
|
| }
|
| - sqlite3_free(pCtx->lockProxyPath);
|
| + sqlite3DbFree(0, pCtx->lockProxyPath);
|
| sqlite3_free(pCtx->conchFilePath);
|
| - sqlite3_free(pCtx->dbPath);
|
| + sqlite3DbFree(0, pCtx->dbPath);
|
| /* restore the original locking context and pMethod then close it */
|
| pFile->lockingContext = pCtx->oldLockingContext;
|
| pFile->pMethod = pCtx->pOldMethod;
|
| @@ -5380,7 +6693,7 @@ int sqlite3_os_init(void){
|
| ** that filesystem time.
|
| */
|
| #define UNIXVFS(VFSNAME, FINDER) { \
|
| - 1, /* iVersion */ \
|
| + 3, /* iVersion */ \
|
| sizeof(unixFile), /* szOsFile */ \
|
| MAX_PATHNAME, /* mxPathname */ \
|
| 0, /* pNext */ \
|
| @@ -5397,7 +6710,11 @@ int sqlite3_os_init(void){
|
| unixRandomness, /* xRandomness */ \
|
| unixSleep, /* xSleep */ \
|
| unixCurrentTime, /* xCurrentTime */ \
|
| - unixGetLastError /* xGetLastError */ \
|
| + unixGetLastError, /* xGetLastError */ \
|
| + unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \
|
| + unixSetSystemCall, /* xSetSystemCall */ \
|
| + unixGetSystemCall, /* xGetSystemCall */ \
|
| + unixNextSystemCall, /* xNextSystemCall */ \
|
| }
|
|
|
| /*
|
| @@ -5415,7 +6732,7 @@ int sqlite3_os_init(void){
|
| #endif
|
| UNIXVFS("unix-none", nolockIoFinder ),
|
| UNIXVFS("unix-dotfile", dotlockIoFinder ),
|
| - UNIXVFS("unix-wfl", posixWflIoFinder ),
|
| + UNIXVFS("unix-excl", posixIoFinder ),
|
| #if OS_VXWORKS
|
| UNIXVFS("unix-namedsem", semIoFinder ),
|
| #endif
|
| @@ -5427,11 +6744,16 @@ int sqlite3_os_init(void){
|
| #endif
|
| #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| UNIXVFS("unix-afp", afpIoFinder ),
|
| + UNIXVFS("unix-nfs", nfsIoFinder ),
|
| UNIXVFS("unix-proxy", proxyIoFinder ),
|
| #endif
|
| };
|
| unsigned int i; /* Loop counter */
|
|
|
| + /* Double-check that the aSyscall[] array has been constructed
|
| + ** correctly. See ticket [bb3a86e890c8e96ab] */
|
| + assert( ArraySize(aSyscall)==16 );
|
| +
|
| /* Register all VFSes defined in the aVfs[] array */
|
| for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
| sqlite3_vfs_register(&aVfs[i], i==0);
|
|
|