Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -38,11 +38,11 @@ ** + for proxy locks (MacOSX only) ** * The routine used to detect an appropriate locking style ** * sqlite3_file methods not associated with locking ** * Implementations of sqlite3_os_init() and sqlite3_os_end() ** -** $Id: os_unix.c,v 1.222 2008/11/28 15:37:20 drh Exp $ +** $Id: os_unix.c,v 1.223 2008/11/29 00:56:53 drh Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ /* @@ -154,53 +154,10 @@ /* ** Maximum supported path-length. */ #define MAX_PATHNAME 512 -/* -** The locking styles are associated with the different file locking -** capabilities supported by different file systems. -** -** POSIX support for shared and exclusive byte-range locks -** -** NONE no locking will be attempted, this is only used for -** read-only file systems currently -** -** DOTLOCK isn't a true locking style, it refers to the use of a special -** file named the same as the database file with a '.lock' -** extension, this can be used on file systems that do not -** offer any reliable file locking -** -** FLOCK only a single file-global exclusive lock (Not on VxWorks) -** -** NAMEDSEM similar to DOTLOCK but uses a named semaphore instead of an -** indicator file. (VxWorks only) -** -** AFP support exclusive byte-range locks (MacOSX only) -** -** PROXY uses a second file to represent the lock state of the database -** file which is never actually locked, a third file controls -** access to the proxy (MacOSX only) -** -** Note that because FLOCK and NAMEDSEM are never used together, they -** share the same code number (3). The locking mode numbering is -** chosen so that a set of locking modes that are contiguous integers -** from 1 to N. On generic unix systems without flock() support, -** the modes are 1..3. On generic unix with flock() support, the modes -** are 1..4. On VxWorks, the modes are 1..4. On MacOSX the modes -** are 1..6. -*/ -#define LOCKING_STYLE_POSIX 1 -#define LOCKING_STYLE_NONE 2 -#define LOCKING_STYLE_DOTFILE 3 -#define LOCKING_STYLE_FLOCK 4 -#define LOCKING_STYLE_NAMEDSEM 4 -#define LOCKING_STYLE_AFP 5 -#define LOCKING_STYLE_PROXY 6 - -#define LOCKING_STYLE_AUTOMATIC 0 /* Choose lock style automatically */ - /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) @@ -860,61 +817,10 @@ static void testThreadLockingBehavior(int fd_orig){ UNUSED_PARAMETER(fd_orig); threadsOverrideEachOthersLocks = 1; } #endif /* SQLITE_THERADSAFE && defined(__linux__) */ - -/* -** 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 where one thread is -** unable to override locks created by a different thread. RedHat9 is -** an example of such a system. -** -** 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; - } -} -#else /* if not SQLITE_THREADSAFE */ - /* On single-threaded builds, ownership transfer is a no-op */ -# define transferOwnership(X) SQLITE_OK -#endif /* SQLITE_THREADSAFE */ - /* ** Release a unixLockInfo structure previously allocated by findLockInfo(). */ static void releaseLockInfo(struct unixLockInfo *pLock){ @@ -1085,10 +991,58 @@ 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; + } +} +#else /* if not SQLITE_THREADSAFE */ + /* On single-threaded builds, ownership transfer is a no-op */ +# define transferOwnership(X) SQLITE_OK +#endif /* SQLITE_THREADSAFE */ + /* ** 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 @@ -1644,20 +1598,33 @@ ** (2) An application crash or power loss can leave stale lock files ** sitting around that need to be cleared manually. ** ** Nevertheless, a dotlock is an appropriate locking mode for use if no ** other locking strategy is available. +** +** Dotfile locking works by creating a file in the same directory as the +** database and with the same name but with a ".lock" extension added. +** The existance of a lock file implies an EXCLUSIVE lock. All other lock +** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. */ /* ** The file suffix added to the data base filename in order to create the ** lock file. */ #define DOTLOCK_SUFFIX ".lock" -/* Dotlock-style reserved lock checking following the behavior of -** unixCheckReservedLock, see the unixCheckReservedLock function comments */ +/* +** 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 +** is set to SQLITE_OK unless an I/O error occurs during lock checking. +** +** In dotfile locking, either a lock exists or it does not. So in this +** variation of CheckReservedLock(), *pResOut is set to true if any lock +** is held on the file and false if the file is unlocked. +*/ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { int rc = SQLITE_OK; int reserved = 0; unixFile *pFile = (unixFile*)id; @@ -1665,59 +1632,67 @@ assert( pFile ); /* Check if a thread in this process holds such a lock */ if( pFile->locktype>SHARED_LOCK ){ - reserved = 1; - } - - /* Otherwise see if some other process holds it. */ - if( !reserved ){ - char *zLockFile = (char *)pFile->lockingContext; - struct stat statBuf; - - if( lstat(zLockFile, &statBuf)==0 ){ - /* file exists, someone else has the lock */ - reserved = 1; - }else{ - /* file does not exist, we could have it if we want it */ - int tErrno = errno; - if( ENOENT != tErrno ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; - } - } - } - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); - + /* 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; + } + OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; } +/* +** Lock the file with the lock specified by parameter locktype - one +** of the following: +** +** (1) SHARED_LOCK +** (2) RESERVED_LOCK +** (3) PENDING_LOCK +** (4) EXCLUSIVE_LOCK +** +** Sometimes when requesting one lock state, additional lock states +** are inserted in between. The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal. The following chart shows the allowed +** transitions and the inserted intermediate states: +** +** UNLOCKED -> SHARED +** SHARED -> RESERVED +** SHARED -> (PENDING) -> EXCLUSIVE +** RESERVED -> (PENDING) -> EXCLUSIVE +** PENDING -> EXCLUSIVE +** +** This routine will only increase a lock. Use the sqlite3OsUnlock() +** routine to lower a locking level. +** +** 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) { unixFile *pFile = (unixFile*)id; int fd; char *zLockFile = (char *)pFile->lockingContext; - int rc=SQLITE_OK; + 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) { + + /* 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 !OS_VXWORKS /* Always update the timestamp on the old file */ utimes(zLockFile, NULL); #endif - rc = SQLITE_OK; - goto dotlock_end_lock; - } - - /* check to see if lock file already exists */ - struct stat statBuf; - if (lstat(zLockFile,&statBuf) == 0){ - rc = SQLITE_BUSY; /* it does, busy */ - goto dotlock_end_lock; + return SQLITE_OK; } /* grab an exclusive lock */ fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); if( fd<0 ){ @@ -1729,24 +1704,31 @@ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } - goto dotlock_end_lock; + return rc; } if( close(fd) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; } /* got it, set the type and return ok */ pFile->locktype = locktype; - - dotlock_end_lock: return rc; } +/* +** Lower the locking level on file descriptor pFile to locktype. locktype +** 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. +** +** When the locking level reaches NO_LOCK, delete the lock file. +*/ static int dotlockUnlock(sqlite3_file *id, int locktype) { unixFile *pFile = (unixFile*)id; char *zLockFile = (char *)pFile->lockingContext; assert( pFile ); @@ -1756,19 +1738,22 @@ /* no-op if possible */ if( pFile->locktype==locktype ){ return SQLITE_OK; } - - /* shared can just be set because we always have an exclusive */ - if (locktype==SHARED_LOCK) { - pFile->locktype = locktype; + + /* 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; return SQLITE_OK; } - /* no, really, unlock. */ - if (unlink(zLockFile) ) { + /* To fully unlock the database, delete the lock file */ + assert( locktype==NO_LOCK ); + if( unlink(zLockFile) ){ int rc, tErrno = errno; if( ENOENT != tErrno ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); } if( IS_LOCK_ERROR(rc) ){ @@ -1805,10 +1790,11 @@ ** ** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if ** compiling for VXWORKS. */ #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +#include /* ** The flockLockingContext is not used */ typedef void flockLockingContext; @@ -1868,11 +1854,10 @@ return rc; } static int flockLock(sqlite3_file *id, int locktype) { int rc = SQLITE_OK; - int lrc; unixFile *pFile = (unixFile*)id; assert( pFile ); /* if we already have a lock, it is exclusive. @@ -2627,43 +2612,16 @@ #if defined(__DARWIN__) && SQLITE_ENABLE_LOCKING_STYLE static int getDbPathForUnixFile(unixFile *pFile, char *dbPath); static int getLockPath(const char *dbPath, char *lPath, size_t maxLen); -static sqlite3_io_methods *ioMethodForLockingStyle(int style); static int createProxyUnixFile(const char *path, unixFile **ppFile); static int fillInUnixFile(sqlite3_vfs *pVfs, int h, int dirfd, sqlite3_file *pId, const char *zFilename, int noLock, int isDelete); static int takeConch(unixFile *pFile); static int releaseConch(unixFile *pFile); static int unixRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf); -/* -** Tests a byte-range locking query to see if byte range locks are -** supported, if not we fall back to dotlockLockingStyle. -** On vxWorks we fall back to semLockingStyle. -*/ -static int testLockingStyle(int fd){ - struct flock lockInfo; - - /* Test byte-range lock using fcntl(). If the call succeeds, - ** assume that the file-system supports POSIX style locks. - */ - lockInfo.l_len = 1; - lockInfo.l_start = 0; - lockInfo.l_whence = SEEK_SET; - lockInfo.l_type = F_RDLCK; - if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) { - return LOCKING_STYLE_POSIX; - } - - /* Testing for flock() can give false positives. So if if the above - ** test fails, then we fall back to using dot-file style locking (or - ** named-semaphore locking on vxworks). - */ - return (OS_VXWORKS ? LOCKING_STYLE_NAMEDSEM : LOCKING_STYLE_DOTFILE); -} - #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ int sqlite3_hostid_num = 0; #endif @@ -3020,12 +2978,11 @@ if( rc==SQLITE_OK ){ pCtx->conchHeld = 1; if( tLockPath ){ pCtx->lockProxyPath = sqlite3DbStrDup(0, tLockPath); - if( pCtx->lockProxy->pMethod == - ioMethodForLockingStyle(LOCKING_STYLE_AFP) ){ + if( pCtx->lockProxy->pMethod == &afpIoMethods ){ ((afpLockingContext *)pCtx->lockProxy->lockingContext)->dbPath = pCtx->lockProxyPath; } } } else { @@ -3210,11 +3167,11 @@ */ pCtx->dbPath = sqlite3DbStrDup(0, dbPath); pCtx->oldLockingContext = pFile->lockingContext; pFile->lockingContext = pCtx; pCtx->pOldMethod = pFile->pMethod; - pFile->pMethod = ioMethodForLockingStyle(LOCKING_STYLE_PROXY); + pFile->pMethod = &proxyIoMethods; }else{ if( pCtx->conchFile ){ rc = pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); if( rc ) return rc; sqlite3_free(pCtx->conchFile); @@ -3644,14 +3601,18 @@ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((unixFile*)id)->locktype; return SQLITE_OK; } + case SQLITE_LAST_ERRNO: { + *(int*)pArg = ((unixFile*)id)->lastErrno; + return SQLITE_OK; + } +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__DARWIN__) case SQLITE_GET_LOCKPROXYFILE: { -#if SQLITE_ENABLE_LOCKING_STYLE unixFile *pFile = (unixFile*)id; - if( pFile->pMethod == ioMethodForLockingStyle(LOCKING_STYLE_PROXY) ){ + if( pFile->pMethod == &proxyIoMethods ){ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; takeConch(pFile); if( pCtx->lockProxyPath ){ *(const char **)pArg = pCtx->lockProxyPath; }else{ @@ -3658,53 +3619,45 @@ *(const char **)pArg = ":auto: (not held)"; } } else { *(const char **)pArg = NULL; } -#else - *(void**)pArg = NULL; -#endif return SQLITE_OK; } case SQLITE_SET_LOCKPROXYFILE: { -#if SQLITE_ENABLE_LOCKING_STYLE unixFile *pFile = (unixFile*)id; int rc = SQLITE_OK; - int isProxyStyle = (pFile->pMethod == ioMethodForLockingStyle(LOCKING_STYLE_PROXY)); + int isProxyStyle = (pFile->pMethod == &proxyIoMethods); if( pArg==NULL || (const char *)pArg==0 ){ if( isProxyStyle ){ - // turn off proxy locking - not supported + /* turn off proxy locking - not supported */ rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; }else{ - // turn off proxy locking - already off - NOOP + /* turn off proxy locking - already off - NOOP */ rc = SQLITE_OK; } }else{ const char *proxyPath = (const char *)pArg; if( isProxyStyle ){ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; - if( !strcmp(pArg, ":auto:") || (pCtx->lockProxyPath && !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) ){ + if( !strcmp(pArg, ":auto:") + || (pCtx->lockProxyPath && + !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) + ){ rc = SQLITE_OK; }else{ rc = switchLockProxyPath(pFile, proxyPath); } }else{ - // turn on proxy file locking + /* turn on proxy file locking */ rc = transformUnixFileForLockProxy(pFile, proxyPath); } } return rc; -#else - return SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; -#endif - } - case SQLITE_LAST_ERRNO: { - *(int*)pArg = ((unixFile*)id)->lastErrno; - return SQLITE_OK; - } - + } +#endif } return SQLITE_ERROR; } /* @@ -3735,139 +3688,187 @@ ** ********************** End sqlite3_file Methods ******************************* ******************************************************************************/ /* -** The following constant array describes all of the methods for the -** sqlite3_file object for each of the various locking modes. +** Each instance of this macro generates two objects: +** +** * A constant sqlite3_io_methods object call METHOD that has locking +** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. ** -** The order in which the methods are defined is important and must -** agree with the numeric values of the method identifier constants. -** For example, LOCKING_STYLE_UNIX has a numeric value of zero, so -** it must be the 0-th entry in the array. -*/ -#define IOMETHODS(xClose, xLock, xUnlock, xCheckReservedLock) { \ - 1, /* iVersion */ \ - xClose, /* xClose */ \ - unixRead, /* xRead */ \ - unixWrite, /* xWrite */ \ - unixTruncate, /* xTruncate */ \ - unixSync, /* xSync */ \ - unixFileSize, /* xFileSize */ \ - xLock, /* xLock */ \ - xUnlock, /* xUnlock */ \ - xCheckReservedLock, /* xCheckReservedLock */ \ - unixFileControl, /* xFileControl */ \ - unixSectorSize, /* xSectorSize */ \ - unixDeviceCharacteristics /* xDeviceCapabilities */ \ -} -static sqlite3_io_methods aIoMethod[] = { - IOMETHODS(unixClose, unixLock, unixUnlock, unixCheckReservedLock), - IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock), - IOMETHODS(dotlockClose, dotlockLock, dotlockUnlock,dotlockCheckReservedLock), +** * 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) \ +static const sqlite3_io_methods METHOD = { \ + 1, /* iVersion */ \ + CLOSE, /* xClose */ \ + unixRead, /* xRead */ \ + unixWrite, /* xWrite */ \ + unixTruncate, /* xTruncate */ \ + unixSync, /* xSync */ \ + unixFileSize, /* xFileSize */ \ + LOCK, /* xLock */ \ + UNLOCK, /* xUnlock */ \ + CKLOCK, /* xCheckReservedLock */ \ + unixFileControl, /* xFileControl */ \ + unixSectorSize, /* xSectorSize */ \ + unixDeviceCharacteristics /* xDeviceCapabilities */ \ +}; \ +static const sqlite3_io_methods *FINDER(const char *z, int h){ \ + UNUSED_PARAMETER(z); UNUSED_PARAMETER(h); \ + return &METHOD; \ +} + +/* +** Here are all of the sqlite3_io_methods objects for each of the +** locking strategies. Functions that return pointers to these methods +** are also created. +*/ +IOMETHODS( + posixIoFinder, /* Finder function name */ + posixIoMethods, /* sqlite3_io_methods object name */ + unixClose, /* xClose method */ + unixLock, /* xLock method */ + unixUnlock, /* xUnlock method */ + unixCheckReservedLock /* xCheckReservedLock method */ +); +IOMETHODS( + nolockIoFinder, /* Finder function name */ + nolockIoMethods, /* sqlite3_io_methods object name */ + nolockClose, /* xClose method */ + nolockLock, /* xLock method */ + nolockUnlock, /* xUnlock method */ + nolockCheckReservedLock /* xCheckReservedLock method */ +); +IOMETHODS( + dotlockIoFinder, /* Finder function name */ + dotlockIoMethods, /* sqlite3_io_methods object name */ + dotlockClose, /* xClose method */ + dotlockLock, /* xLock method */ + dotlockUnlock, /* xUnlock method */ + dotlockCheckReservedLock /* xCheckReservedLock method */ +); + +#if SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + flockIoFinder, /* Finder function name */ + flockIoMethods, /* sqlite3_io_methods object name */ + flockClose, /* xClose method */ + flockLock, /* xLock method */ + flockUnlock, /* xUnlock method */ + flockCheckReservedLock /* xCheckReservedLock method */ +); +#endif + #if OS_VXWORKS - IOMETHODS(semClose, semLock, semUnlock, semCheckReservedLock), -#elif SQLITE_ENABLE_LOCKING_STYLE - IOMETHODS(flockClose, flockLock, flockUnlock, flockCheckReservedLock), +IOMETHODS( + semIoFinder, /* Finder function name */ + semIoMethods, /* sqlite3_io_methods object name */ + semClose, /* xClose method */ + semLock, /* xLock method */ + semUnlock, /* xUnlock method */ + semCheckReservedLock /* xCheckReservedLock method */ +); +#endif + +#if defined(__DARWIN__) && SQLITE_ENABLE_LOCKING_STYLE +IOMETHODS( + afpIoFinder, /* Finder function name */ + afpIoMethods, /* sqlite3_io_methods object name */ + afpClose, /* xClose method */ + afpLock, /* xLock method */ + afpUnlock, /* xUnlock method */ + afpCheckReservedLock /* xCheckReservedLock method */ +); +IOMETHODS( + proxyIoFinder, /* Finder function name */ + proxyIoMethods, /* sqlite3_io_methods object name */ + proxyClose, /* xClose method */ + proxyLock, /* xLock method */ + proxyUnlock, /* xUnlock method */ + proxyCheckReservedLock /* xCheckReservedLock method */ +); #endif + + #if defined(__DARWIN__) && SQLITE_ENABLE_LOCKING_STYLE - IOMETHODS(afpClose, afpLock, afpUnlock, afpCheckReservedLock), - IOMETHODS(proxyClose, proxyLock, proxyUnlock, proxyCheckReservedLock), +/* +** This procedure attempts to determine the best locking strategy for +** the given database file. It then returns the sqlite3_io_methods +** object that implements that strategy. +** +** This is for MacOSX only. +*/ +static const sqlite3_io_methods *autolockIoFinder( + const char *filePath, /* name of the database file */ + int fd /* file descriptor open on the database file */ +){ + static const struct Mapping { + const char *zFilesystem; + const sqlite3_io_methods *pMethods; + } aMap[] = { + { "hfs", &posixIoMethods }, + { "ufs", &posixIoMethods }, + { "afpfs", &afpIoMethods }, +#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB + { "smbfs", &afpIoMethods }, +#else + { "smbfs", &flockIoMethods }, #endif - /* The order of the IOMETHODS macros above is important. It must be the - ** same order as the LOCKING_STYLE numbers + { "webdav", &nolockIoMethods }, + { 0, 0 } + }; + int i; + struct statfs fsInfo; + struct flock lockInfo; + + if( !filePath ){ + return &nolockIoMethods; + } + if( statfs(filePath, &fsInfo) != -1 ){ + if( fsInfo.f_flags & MNT_RDONLY ){ + return &nolockIoMethods; + } + for(i=0; aMap[i].zFilesystem; i++){ + if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ + return aMap[i].pMethods; + } + } + } + + /* Default case. Handles, amongst others, "nfs". + ** Test byte-range lock using fcntl(). If the call succeeds, + ** assume that the file-system supports POSIX style locks. */ -}; + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) { + return &posixIoMethods; + }else{ + return &dotlockIoMethods; + } +} +#endif /* defined(__DARWIN__) && SQLITE_ENABLE_LOCKING_STYLE */ + +/* +** An abstract type for a pointer to a IO method finder function: +*/ +typedef const sqlite3_io_methods *(*finder_type)(const char*,int); + /**************************************************************************** **************************** sqlite3_vfs methods **************************** ** ** This division contains the implementation of methods on the ** sqlite3_vfs object. */ - -/* -** If SQLITE_ENABLE_LOCKING_STYLE is defined, this function Examines the -** f_fstypename entry in the statfs structure as returned by stat() for -** the file system hosting the database file and selects the appropriate -** locking style based on its value. These values and assignments are -** based on Darwin/OSX behavior and have not been thoroughly tested on -** other systems. -** -** If SQLITE_ENABLE_LOCKING_STYLE is not defined, this function always -** returns LOCKING_STYLE_POSIX. -*/ -#if SQLITE_ENABLE_LOCKING_STYLE -static int detectLockingStyle( - sqlite3_vfs *pVfs, - const char *filePath, - int fd -){ -#if OS_VXWORKS - if( !filePath ){ - return LOCKING_STYLE_NONE; - } - if( pVfs->pAppData ){ - return SQLITE_PTR_TO_INT(pVfs->pAppData); - } - if (access(filePath, 0) != -1){ - return testLockingStyle(fd); - } -#else - struct Mapping { - const char *zFilesystem; - int eLockingStyle; - } aMap[] = { - { "hfs", LOCKING_STYLE_POSIX }, - { "ufs", LOCKING_STYLE_POSIX }, - { "afpfs", LOCKING_STYLE_AFP }, -#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB - { "smbfs", LOCKING_STYLE_AFP }, -#else - { "smbfs", LOCKING_STYLE_FLOCK }, -#endif - { "webdav", LOCKING_STYLE_NONE }, - { 0, 0 } - }; - int i; - struct statfs fsInfo; - - if( !filePath ){ - return LOCKING_STYLE_NONE; - } - if( pVfs && pVfs->pAppData ){ - return SQLITE_PTR_TO_INT(pVfs->pAppData); - } - - if( statfs(filePath, &fsInfo) != -1 ){ - if( fsInfo.f_flags & MNT_RDONLY ){ - return LOCKING_STYLE_NONE; - } - for(i=0; aMap[i].zFilesystem; i++){ - if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ - return aMap[i].eLockingStyle; - } - } - } - - /* Default case. Handles, amongst others, "nfs". */ - return testLockingStyle(fd); -#endif /* if OS_VXWORKS */ - return LOCKING_STYLE_POSIX; -} -#else - #define detectLockingStyle(x,y,z) LOCKING_STYLE_POSIX -#endif /* if SQLITE_ENABLE_LOCKING_STYLE */ - /* ** Initialize the contents of the unixFile structure pointed to by pId. -** -** When locking extensions are enabled, the filepath and locking style -** are needed to determine the unixFile pMethod to use for locking operations. -** The locking-style specific lockingContext data structure is created -** and assigned here also. */ static int fillInUnixFile( sqlite3_vfs *pVfs, /* Pointer to vfs object */ int h, /* Open file descriptor of file being opened */ int dirfd, /* Directory file descriptor */ @@ -3874,11 +3875,11 @@ 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 eLockingStyle; + const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pLock==NULL ); assert( pNew->pOpen==NULL ); @@ -3885,13 +3886,19 @@ /* Parameter isDelete is only used on vxworks. Parameter pVfs is only ** used if ENABLE_LOCKING_STYLE is defined. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ - if( !OS_VXWORKS ) UNUSED_PARAMETER(isDelete); - if( !SQLITE_ENABLE_LOCKING_STYLE ) UNUSED_PARAMETER(pVfs); - if( !OS_VXWORKS && !SQLITE_ENABLE_LOCKING_STYLE ) UNUSED_PARAMETER(zFilename); +#if !OS_VXWORKS + UNUSED_PARAMETER(isDelete); +#endif +#if !SQLITE_ENABLE_LOCKING_STYLE + UNUSED_PARAMETER(pVfs); +#endif +#if !OS_VXWORKS && !SQLITE_ENABLE_LOCKING_STYLE + UNUSED_PARAMETER(zFilename); +#endif OSTRACE3("OPEN %-3d %s\n", h, zFilename); pNew->h = h; pNew->dirfd = dirfd; SET_THREADID(pNew); @@ -3903,103 +3910,90 @@ rc = SQLITE_NOMEM; } #endif if( noLock ){ - eLockingStyle = LOCKING_STYLE_NONE; + pLockingStyle = &nolockIoMethods; }else{ - eLockingStyle = detectLockingStyle(pVfs, zFilename, h); + pLockingStyle = (*(finder_type)pVfs->pAppData)(zFilename, h); #if SQLITE_ENABLE_LOCKING_STYLE /* Cache zFilename in the locking context (AFP and dotlock override) for ** proxyLock activation is possible (remote proxy is based on db name) ** zFilename remains valid until file is closed, to support */ pNew->lockingContext = (void*)zFilename; #endif } - - switch( eLockingStyle ){ + if( pLockingStyle == &posixIoMethods ){ + unixEnterMutex(); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + unixLeaveMutex(); + } - case LOCKING_STYLE_POSIX: { +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__DARWIN__) + else if( pLockingStyle == &apfIoMethods ){ + /* AFP locking uses the file path so it needs to be included in + ** the afpLockingContext. + */ + afpLockingContext *pCtx; + pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); + if( pCtx==0 ){ + rc = SQLITE_NOMEM; + }else{ + /* NB: zFilename exists and remains valid until the file is closed + ** according to requirement F11141. So we do not need to make a + ** copy of the filename. */ + pCtx->dbPath = zFilename; + srandomdev(); unixEnterMutex(); - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - unixLeaveMutex(); - break; + rc = findLockInfo(pNew, NULL, &pNew->pOpen); + unixLeaveMutex(); } + } +#endif #if SQLITE_ENABLE_LOCKING_STYLE - -#if !OS_VXWORKS - case LOCKING_STYLE_AFP: { - /* AFP locking uses the file path so it needs to be included in - ** the afpLockingContext. - */ - afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); - if( pCtx==0 ){ - rc = SQLITE_NOMEM; - }else{ - /* NB: zFilename exists and remains valid until the file is closed - ** according to requirement F11141. So we do not need to make a - ** copy of the filename. */ - pCtx->dbPath = zFilename; - srandomdev(); - unixEnterMutex(); - rc = findLockInfo(pNew, NULL, &pNew->pOpen); - unixLeaveMutex(); - } - break; - } -#endif - - case LOCKING_STYLE_DOTFILE: { - /* Dotfile locking uses the file path so it needs to be included in - ** the dotlockLockingContext - */ - char *zLockFile; - int nFilename; - nFilename = strlen(zFilename) + 6; - zLockFile = (char *)sqlite3_malloc(nFilename); - if( zLockFile==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); - } - pNew->lockingContext = zLockFile; - break; - } + else if( pLockingStyle == &dotlockIoMethods ){ + /* Dotfile locking uses the file path so it needs to be included in + ** the dotlockLockingContext + */ + char *zLockFile; + int nFilename; + nFilename = strlen(zFilename) + 6; + zLockFile = (char *)sqlite3_malloc(nFilename); + if( zLockFile==0 ){ + rc = SQLITE_NOMEM; + }else{ + sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); + } + pNew->lockingContext = zLockFile; + } +#endif #if OS_VXWORKS - case LOCKING_STYLE_NAMEDSEM: { - /* Named semaphore locking uses the file path so it needs to be - ** included in the semLockingContext - */ - unixEnterMutex(); - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ - char *zSemName = pNew->pOpen->aSemName; - int n; - sqlite3_snprintf(MAX_PATHNAME, zSemName, "%s.sem", - pNew->pId->zCanonicalName); - for( n=0; zSemName[n]; n++ ) - if( zSemName[n]=='/' ) zSemName[n] = '_'; - pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); - if( pNew->pOpen->pSem == SEM_FAILED ){ - rc = SQLITE_NOMEM; - pNew->pOpen->aSemName[0] = '\0'; - } - } - unixLeaveMutex(); - break; - } -#endif - - case LOCKING_STYLE_FLOCK: - case LOCKING_STYLE_NONE: - break; -#endif - } + else if( pLockingStyle == &semIoMethods ){ + /* Named semaphore locking uses the file path so it needs to be + ** included in the semLockingContext + */ + unixEnterMutex(); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ + char *zSemName = pNew->pOpen->aSemName; + int n; + sqlite3_snprintf(MAX_PATHNAME, zSemName, "%s.sem", + pNew->pId->zCanonicalName); + for( n=0; zSemName[n]; n++ ) + if( zSemName[n]=='/' ) zSemName[n] = '_'; + pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); + if( pNew->pOpen->pSem == SEM_FAILED ){ + rc = SQLITE_NOMEM; + pNew->pOpen->aSemName[0] = '\0'; + } + } + unixLeaveMutex(); + } +#endif pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ unlink(zFilename); @@ -4009,38 +4003,36 @@ #endif if( rc!=SQLITE_OK ){ if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ close(h); }else{ - pNew->pMethod = &aIoMethod[eLockingStyle-1]; + pNew->pMethod = pLockingStyle; OpenCounter(+1); } return rc; } #if SQLITE_ENABLE_LOCKING_STYLE -static sqlite3_io_methods *ioMethodForLockingStyle(int style){ - return &aIoMethod[style]; -} - static int getDbPathForUnixFile(unixFile *pFile, char *dbPath){ - if( pFile->pMethod==ioMethodForLockingStyle(LOCKING_STYLE_AFP) ){ +#if defined(__DARWIN__) + if( pFile->pMethod == &afpIoMethods ){ /* afp style keeps a reference to the db path in the filePath field ** of the struct */ - strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, - MAXPATHLEN); - return SQLITE_OK; - } - if( pFile->pMethod==ioMethodForLockingStyle(LOCKING_STYLE_DOTFILE) ){ + assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); + strcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath) + }else +#endif + if( pFile->pMethod == &dotlockIoMethods ){ /* dot lock style uses the locking context to store the dot lock ** file path */ int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX); - strlcpy(dbPath, (char *)pFile->lockingContext, len + 1); - return SQLITE_OK; + memcpy(dbPath, (char *)pFile->lockingContext, len + 1); + }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); } - /* all other styles use the locking context to store the db file path */ - strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); return SQLITE_OK; } #endif /* @@ -4281,12 +4273,12 @@ if( zPath!=NULL && !noLock ){ char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); int useProxy = 0; /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, - ** 0 means never use proxy, NULL means use proxy for non-local files only - */ + ** 0 means never use proxy, NULL means use proxy for non-local files only + */ if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ struct statfs fsInfo; @@ -4593,17 +4585,17 @@ /* Macro to define the static contents of an sqlite3_vfs structure for ** the unix backend. The two parameters are the values to use for ** the sqlite3_vfs.zName and sqlite3_vfs.pAppData fields, respectively. ** */ - #define UNIXVFS(zVfsName, pVfsAppData) { \ + #define UNIXVFS(VFSNAME, FINDER) { \ 1, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ - zVfsName, /* zName */ \ - (void *)pVfsAppData, /* pAppData */ \ + VFSNAME, /* zName */ \ + (void*)FINDER, /* pAppData */ \ unixOpen, /* xOpen */ \ unixDelete, /* xDelete */ \ unixAccess, /* xAccess */ \ unixFullPathname, /* xFullPathname */ \ unixDlOpen, /* xDlOpen */ \ @@ -4614,25 +4606,29 @@ unixSleep, /* xSleep */ \ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError /* xGetLastError */ \ } - int i; + unsigned int i; static sqlite3_vfs aVfs[] = { - UNIXVFS("unix", LOCKING_STYLE_AUTOMATIC), - UNIXVFS("unix-posix", LOCKING_STYLE_POSIX), - UNIXVFS("unix-none", LOCKING_STYLE_NONE), - UNIXVFS("unix-dotfile", LOCKING_STYLE_DOTFILE), +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__DARWIN__) + UNIXVFS("unix", autolockIoFinder ), +#else + UNIXVFS("unix", posixIoFinder ), +#endif + UNIXVFS("unix-none", nolockIoFinder ), + UNIXVFS("unix-dotfile", dotlockIoFinder ), #if OS_VXWORKS - UNIXVFS("unix-namedsem",LOCKING_STYLE_NAMEDSEM), + UNIXVFS("unix-namedsem", semIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE - UNIXVFS("unix-flock", LOCKING_STYLE_FLOCK), + UNIXVFS("unix-posix", posixIoFinder ), + UNIXVFS("unix-flock", flockIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__DARWIN__) - UNIXVFS("unix-afp", LOCKING_STYLE_AFP), - UNIXVFS("unix-proxy", LOCKING_STYLE_PROXY) + UNIXVFS("unix-afp", afpIoFinder ), + UNIXVFS("unix-proxy", proxyIoFinder ), #endif }; for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); } Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.332 2008/11/28 15:37:20 drh Exp $ +** $Id: test1.c,v 1.333 2008/11/29 00:56:53 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include @@ -4525,24 +4525,33 @@ Tcl_GetStringFromObj(objv[0], 0), " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; -#ifdef SQLITE_ENABLE_LOCKING_STYLE - { +#if defined(SQLITE_ENABLE_LOCKING_STYLE) && defined(__DARWIN__) + { char *proxyPath = "test.proxy"; char *testPath; - rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); - if( rc ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } + rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); + if( rc ){ + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; + } rc = sqlite3_file_control(db, NULL, SQLITE_GET_LOCKPROXYFILE, &testPath); if( strncmp(proxyPath,testPath,11) ) { - Tcl_AppendResult(interp, "Lock proxy file did not match the previously assigned value", 0); + Tcl_AppendResult(interp, "Lock proxy file did not match the " + "previously assigned value", 0); + return TCL_ERROR; + } + if( rc ){ + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } - if( rc ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } - rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); - if( rc ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } + rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); + if( rc ){ + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); + return TCL_ERROR; + } } #endif return TCL_OK; } Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -14,11 +14,11 @@ ** None of the code in this file goes into a deliverable build. ** ** The focus of this file is providing the TCL testing layer ** access to compile-time constants. ** -** $Id: test_config.c,v 1.43 2008/11/21 00:10:35 aswift Exp $ +** $Id: test_config.c,v 1.44 2008/11/29 00:56:54 drh Exp $ */ #include "sqliteLimit.h" #include "sqliteInt.h" @@ -390,14 +390,14 @@ Tcl_SetVar2(interp, "sqlite_options", "schema_version", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE - Tcl_SetVar2(interp, "sqlite_options", "lock_proxy_pragmas", "1", TCL_GLOBAL_ONLY); +#if defined(SQLITE_ENABLE_LOCKING_STYLE) && defined(__DARWIN__) + Tcl_SetVar2(interp,"sqlite_options","lock_proxy_pragmas","1",TCL_GLOBAL_ONLY); #else - Tcl_SetVar2(interp, "sqlite_options", "lock_proxy_pragmas", "0", TCL_GLOBAL_ONLY); + Tcl_SetVar2(interp,"sqlite_options","lock_proxy_pragmas","0",TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_SHARED_CACHE Tcl_SetVar2(interp, "sqlite_options", "shared_cache", "0", TCL_GLOBAL_ONLY);