diff --git a/manifest b/manifest index 2dce09267f..027d4184f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sa\smemory\sallocation\sfails\son\sthe\s%Q\sconversion\sin\ssqlite3_mprintf(),\nmake\ssure\sthe\serror\sis\sreported\sback\sup\sthe\scall\sstack.\s(CVS\s5933) -D 2008-11-20T18:20:28 +C Added\ssupport\sfor\sproxy\sfile\slocking\sstyle\nAdded\spragma\ssupport\sfor\scontrolling\sproxy\sfile\slocking\nAdded\sfile\scontrol\saccess\sto\slast\serrno\sand\sproxy\slocking\nAdded\ssupport\sfor\sTMPDIR\senvironment\svariable\nExtended\sunit\stests\sto\scover\snew\sproxy\slocking\spragmas\sand\sfile\scontrol\sfeatures\s(CVS\s5934) +D 2008-11-21T00:10:35 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 0aa7bbe3be6acc4045706e3bb3fd0b8f38f4a3b5 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -137,7 +137,7 @@ F src/os.c 0b411644b87ad689d7250bbfd1834d99b81a3df4 F src/os.h ef8abeb9afc694b82dbd169a91c9b7e26db3c892 F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60 F src/os_os2.c d12285d66df674c42f6f544a6f7c21bf1a954ee1 -F src/os_unix.c 03c76b5269361adcd68cf9d6713181922535ac6e +F src/os_unix.c bdb0b66407141b7cd3520e92c685ea097b191c0e F src/os_win.c 3dff41670fb9798a869c636626bb7d6d8b6a45bb F src/pager.c db12a8333e54e7bbf62dc621ada5507adb3a6493 F src/pager.h a02ef8e6cc7e78b54874166e5ce786c9d4c489bf @@ -145,21 +145,21 @@ F src/parse.y 2c4758b4c5ead6de8cf7112f5a7cce7561d313fe F src/pcache.c f3121a531745b20f5b824201eb63949a7e2959ac F src/pcache.h f20c3e82dd6da622c3fe296170cb1801f9a2d75a F src/pcache1.c 2453a3e925841fdb610f39bba24def8b2ad4a908 -F src/pragma.c 0a5051029c5b687e2f2669d91aa3281ebcbe4fdb +F src/pragma.c 539e28c90e782fa909a3b3a6849d18a9eb11a548 F src/prepare.c fcadb25d2ad722d87103517333c825b56b79a770 F src/printf.c ce86aed93a704938ca4d0344aaa763271842a4b0 F src/random.c a87afbd598aa877e23ac676ee92fd8ee5c786a51 F src/resolve.c 4af5391d2b4c1d6c583a6805ac6660181de4545b F src/select.c e01537d4d8b8b81a6507e07ebb17fc232dacdda7 F src/shell.c 650d1a87408682280d0e9d014d0d328c59c84b38 -F src/sqlite.h.in e9a0aa2502dfe01bf166956051528f28871474c3 +F src/sqlite.h.in b5d50f12fb9c7460a4ddfef8c1e799afaabefebf F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqliteInt.h beacd5768e55285097fbb419bcd741721b455003 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8 F src/tclsqlite.c 96049bd454f1547abff0a57c45f0dfa57701e076 -F src/test1.c 9b440b38e1710e9be1512c003fa141e0e3948e05 +F src/test1.c d915e5cf26c5ba2a9778ad5b26cbaf97412b8b74 F src/test2.c 897528183edf2839c2a3c991d415905db56f1240 F src/test3.c 88a246b56b824275300e6c899634fbac1dc94b14 F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c @@ -171,7 +171,7 @@ F src/test9.c 904ebe0ed1472d6bad17a81e2ecbfc20017dc237 F src/test_async.c 45024094ed7cf780c5d5dccda645145f95cf78ef F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad F src/test_btree.c d7b8716544611c323860370ee364e897c861f1b0 -F src/test_config.c bc71818f468494ff95ac1fe1f5906a21b25f937b +F src/test_config.c 47c66ced0faa6b18fbab72cbe77098ee04789722 F src/test_devsym.c 802d10e65b4217208cb47059b84adf46318bcdf4 F src/test_func.c a55c4d5479ff2eb5c0a22d4d88e9528ab59c953b F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f @@ -293,11 +293,11 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398 F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041 F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 -F test/exclusive.test 8d32ccf8eaf0260977dc8406bd70080ca2d7e6f8 +F test/exclusive.test ecc64c394f5086d02159d8c0a82520f11420cf6a F test/exclusive2.test 7d2b1c0370f1e1dac4a728bd653f2dea5100fcf6 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/expr.test 135ed46c049916688171e618c5c14312811618d4 -F test/filectrl.test 524853082d5d7fb442599730ec3a0f3f84a3a936 +F test/filectrl.test 8923a6dc7630f31c8a9dd3d3d740aa0922df7bf8 F test/filefmt.test 053b622009fbbb74dd37921ffad374d852c13cd8 F test/fkey1.test c373e4f1ec43416957e0591c4a5cebb63b8a12e7 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb @@ -410,7 +410,8 @@ F test/lock.test 6825aea0b5885578b1b63a3b178803842c4ee9f1 F test/lock2.test 018b846f6f3b3b695fad07e317b7988442b556f4 F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9 F test/lock4.test 09d97d52cae18fadfe631552af9880dac6b3ae90 -F test/lock5.test 904c20aec51d5dbff0a3aec6a4d35c5ae0257449 +F test/lock5.test a6eaac62eb14bc125f9cc0c2d06a80009fc67587 +F test/lock6.test f4e9052b14da3bd6807a757d5aed15c17321031a F test/lookaside.test e69f822f13745f1d5c445c6e30e30f059f30c8e5 F test/main.test 187a9a1b5248ed74a83838c581c15ec6023b555b F test/malloc.test 248fc5e0be632d8db1d771ed5413a93c47212c5a @@ -433,7 +434,7 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb F test/mallocI.test 6e24fe6444bd2999ccc81f984977b44c0d6e5591 F test/mallocJ.test 44dfbbaca731cb933818ad300b4566265d652609 F test/malloc_common.tcl 984baeb6c6b185e798827d1187d426acc2bc4962 -F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8 +F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893 F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217 F test/memsubsys1.test bdc24a38d198679d777ca4efcc089fd3948567a6 @@ -463,7 +464,7 @@ F test/pagesize.test 0d9ff3fedfce6e5ffe8fa7aca9b6d3433a2e843b F test/pcache.test 515b4c26e9f57660357dfff5b6b697acac1abc5f F test/pcache2.test 46efd980a89f737847b99327bda19e08fe11e402 F test/permutations.test 5308a94878efc81a8e8ce133926dfb2c53d19133 -F test/pragma.test 165372b62391d233715cde82d99f34d306f9257f +F test/pragma.test 0f299601c3b15e8941eb48d2f7a43e6678e3f735 F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47 F test/printf.test 262a5acd3158f788e9bdf7f18d718f3af32ff6ef F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 x @@ -491,7 +492,7 @@ F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 F test/selectB.test 31e81ac9af7d224850e0706350f070ecb92fcbc7 F test/selectC.test 44f4e1b2c86e40f8135294a826ec4142766e21c3 F test/server1.test f5b790d4c0498179151ca8a7715a65a7802c859c -F test/shared.test b9f3bbd3ba727c5f1f8c815b7d0199262aacf214 +F test/shared.test 2f6c65de8123c130b92e4e18a516f669eaa02fea F test/shared2.test 0ee9de8964d70e451936a48c41cb161d9134ccf4 F test/shared3.test 9c880afc081d797da514ef64bccf36f3fce2f09c F test/shared4.test d0fadacb50bb6981b2fb9dc6d1da30fa1edddf83 @@ -519,7 +520,7 @@ F test/tableapi.test 505031f15b18a750184d967d2c896cf88fcc969c F test/tclsqlite.test 001682e3c188967fbd790c617991efadf9518386 F test/tempdb.test b88ac8a19823cf771d742bf61eef93ef337c06b1 F test/temptable.test 19b851b9e3e64d91e9867619b2a3f5fffee6e125 -F test/tester.tcl 12fd8394caeb71f7d961707da8668756389bc9d3 +F test/tester.tcl 66c41fc4d8a7f185d9abb21d68821c1f05e41f53 F test/thread001.test dda1d39cea954d7d43f520891d77a93d3325ab58 F test/thread002.test 84c03a9fc4f7a5f92eefe551266afa840c2eb6ae F test/thread003.test e17754799649c2b732c295620dca041c32f01e16 @@ -659,7 +660,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 00b68a1e3164fbf04dabb480c2037be45612b4e4 -R 38e4d91990d6f71d4806b7fd8231a0a6 -U drh -Z c8c595ff0394bbf0b4f1b46a403a8fd7 +P eebacbc9d7d0625dfbe6367046fa4a0ca9c04e74 +R 91b324303811443cc8a01d95fd1a412a +U aswift +Z 225056935d0aa6350680303c08feb50b diff --git a/manifest.uuid b/manifest.uuid index cb60f91a95..24bbfcda51 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -eebacbc9d7d0625dfbe6367046fa4a0ca9c04e74 \ No newline at end of file +b9bc36d3d5e35821ef69c0881a84c0afed253c9e \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index e016fecde0..a75514d8af 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -12,7 +12,7 @@ ** ** This file contains code that is specific to Unix systems. ** -** $Id: os_unix.c,v 1.216 2008/11/19 16:52:44 danielk1977 Exp $ +** $Id: os_unix.c,v 1.217 2008/11/21 00:10:35 aswift Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ @@ -26,7 +26,8 @@ ** * Dot-file locking, ** * flock() locking, ** * AFP locking (OSX only), -** * Named POSIX semaphores (VXWorks only). +** * Named POSIX semaphores (VXWorks only), +** * proxy locking. ** ** SQLITE_ENABLE_LOCKING_STYLE only works on a Mac. It is turned on by ** default on a Mac and disabled on all other posix platforms. @@ -109,6 +110,13 @@ # define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 #endif +/* + ** Default permissions when creating auto proxy dir + */ +#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS +# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 +#endif + /* ** Maximum supported path-length. */ @@ -144,6 +152,9 @@ struct unixFile { int isDelete; /* Delete on close if true */ char *zRealpath; #endif +#if SQLITE_ENABLE_LOCKING_STYLE + int oflags; /* The flags specified at open */ +#endif }; /* @@ -414,10 +425,12 @@ static Hash nameHash; ** 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 -** NO locking means that no locking will be attempted, this is only used for +** NONE locking means that no locking will be attempted, this is only used for ** read-only file systems currently ** NAMEDSEM is similar to DOTLOCK but uses a named semaphore instead of an ** indicator file. +** 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 ** UNSUPPORTED means that no locking will be attempted, this is only used for ** file systems that are known to be unsupported */ @@ -427,6 +440,7 @@ static Hash nameHash; #define LOCKING_STYLE_FLOCK 4 #define LOCKING_STYLE_AFP 5 #define LOCKING_STYLE_NAMEDSEM 6 +#define LOCKING_STYLE_PROXY 7 /* ** Only set the lastErrno if the error code is a real error and not @@ -739,6 +753,31 @@ vxrealpath(const char *pathname, int dostat) #endif #if SQLITE_ENABLE_LOCKING_STYLE +/* +** The proxyLockingContext has the path and file structures for the remote +** and local proxy files in it +*/ +typedef struct proxyLockingContext proxyLockingContext; +struct proxyLockingContext { + unixFile *conchFile; + char *conchFilePath; + unixFile *lockProxy; + char *lockProxyPath; + char *dbPath; + int conchHeld; + void *oldLockingContext; /* preserve the original locking context for close */ + sqlite3_io_methods const *pOldMethod; /* ditto pMethod */ +}; + +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. @@ -806,7 +845,6 @@ static int detectLockingStyle( #else { "smbfs", LOCKING_STYLE_FLOCK }, #endif - { "msdos", LOCKING_STYLE_DOTFILE }, { "webdav", LOCKING_STYLE_NONE }, { 0, 0 } }; @@ -816,7 +854,7 @@ static int detectLockingStyle( if( !filePath ){ return LOCKING_STYLE_NONE; } - if( pVfs->pAppData ){ + if( pVfs && pVfs->pAppData ){ return SQLITE_PTR_TO_INT(pVfs->pAppData); } @@ -838,7 +876,7 @@ static int detectLockingStyle( } #else #define detectLockingStyle(x,y,z) LOCKING_STYLE_POSIX -#endif /* ifdef SQLITE_ENABLE_LOCKING_STYLE */ +#endif /* if SQLITE_ENABLE_LOCKING_STYLE */ /* ** Given a file descriptor, locate lockInfo and openCnt structures that @@ -848,7 +886,7 @@ static int detectLockingStyle( ** Return an appropriate error code. */ static int findLockInfo( - int fd, /* The file descriptor used in the key */ + unixFile *pFile, /* Unix file with file desc used in the key */ #if IS_VXWORKS void *rnam, /* vxWorks realname */ #endif @@ -856,15 +894,18 @@ static int findLockInfo( struct openCnt **ppOpen /* Return the openCnt structure here */ ){ int rc; + int fd; struct lockKey key1; struct openKey key2; struct stat statbuf; struct lockInfo *pLock; struct openCnt *pOpen; + fd = pFile->h; rc = fstat(fd, &statbuf); if( rc!=0 ){ + pFile->lastErrno = errno; #ifdef EOVERFLOW - if( errno==EOVERFLOW ) return SQLITE_NOLFS; + if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; #endif return SQLITE_IOERR; } @@ -883,6 +924,7 @@ static int findLockInfo( write(fd, "S", 1); rc = fstat(fd, &statbuf); if( rc!=0 ){ + pFile->lastErrno = errno; return SQLITE_IOERR; } } @@ -907,28 +949,30 @@ static int findLockInfo( #else key2.ino = statbuf.st_ino; #endif - pLock = lockList; - while( pLock && memcmp(&key1, &pLock->key, sizeof(key1)) ){ - pLock = pLock->pNext; - } - if( pLock==0 ){ - pLock = sqlite3_malloc( sizeof(*pLock) ); - if( pLock==0 ){ - rc = SQLITE_NOMEM; - goto exit_findlockinfo; + if( ppLock!=0 ){ + pLock = lockList; + while( pLock && memcmp(&key1, &pLock->key, sizeof(key1)) ){ + pLock = pLock->pNext; } - pLock->key = key1; - 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++; + if( pLock==0 ){ + pLock = sqlite3_malloc( sizeof(*pLock) ); + if( pLock==0 ){ + rc = SQLITE_NOMEM; + goto exit_findlockinfo; + } + pLock->key = key1; + 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++; + } + *ppLock = pLock; } - *ppLock = pLock; if( ppOpen!=0 ){ pOpen = openList; while( pOpen && memcmp(&key2, &pOpen->key, sizeof(key2)) ){ @@ -1019,9 +1063,9 @@ static int transferOwnership(unixFile *pFile){ if (pFile->pLock != NULL) { releaseLockInfo(pFile->pLock); #if IS_VXWORKS - rc = findLockInfo(pFile->h, pFile->zRealpath, &pFile->pLock, 0); + rc = findLockInfo(pFile, pFile->zRealpath, &pFile->pLock, 0); #else - rc = findLockInfo(pFile->h, &pFile->pLock, 0); + rc = findLockInfo(pFile, &pFile->pLock, 0); #endif OSTRACE5("LOCK %d is now %s(%s,%d)\n", pFile->h, locktypeName(pFile->locktype), @@ -1045,6 +1089,9 @@ static int transferOwnership(unixFile *pFile){ ** one system to another. Since SQLite does not define USE_PREAD ** any any form by default, we will not attempt to define _XOPEN_SOURCE. ** See tickets #2741 and #2681. +** +** To avoid stomping the errno value on a failed read the lastErrno value +** is set before returning. */ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ int got; @@ -1060,11 +1107,19 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ newOffset = lseek(id->h, offset, SEEK_SET); SimulateIOError( newOffset-- ); if( newOffset!=offset ){ + if( newOffet == -1 ){ + ((unixFile*)id)->lastErrno = errno; + }else{ + ((unixFile*)id)->lastErrno = 0; + } return -1; } got = read(id->h, pBuf, cnt); #endif TIMER_END; + if( got<0 ){ + ((unixFile*)id)->lastErrno = errno; + } OSTRACE5("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED); return got; } @@ -1086,8 +1141,10 @@ static int unixRead( if( got==amt ){ return SQLITE_OK; }else if( got<0 ){ + /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; }else{ + ((unixFile*)id)->lastErrno = 0; /* not a system error */ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; @@ -1097,6 +1154,9 @@ static int unixRead( /* ** Seek to the offset in id->offset then read cnt bytes into pBuf. ** Return the number of bytes actually read. Update the offset. +** +** To avoid stomping the errno value on a failed write the lastErrno value +** is set before returning. */ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ int got; @@ -1109,11 +1169,20 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ #else newOffset = lseek(id->h, offset, SEEK_SET); if( newOffset!=offset ){ + if( newOffet == -1 ){ + ((unixFile*)id)->lastErrno = errno; + }else{ + ((unixFile*)id)->lastErrno = 0; + } return -1; } got = write(id->h, pBuf, cnt); #endif TIMER_END; + if( got<0 ){ + ((unixFile*)id)->lastErrno = errno; + } + OSTRACE5("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED); return got; } @@ -1141,8 +1210,10 @@ static int unixWrite( SimulateDiskfullError(( wrote=0, amt=1 )); if( amt>0 ){ if( wrote<0 ){ + /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ + ((unixFile*)id)->lastErrno = 0; /* not a system error */ return SQLITE_FULL; } } @@ -1290,9 +1361,11 @@ static int unixSync(sqlite3_file *id, int flags){ rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ + pFile->lastErrno = errno; return SQLITE_IOERR_FSYNC; } if( pFile->dirfd>=0 ){ + int err; OSTRACE4("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync); #ifndef SQLITE_DISABLE_DIRSYNC @@ -1307,13 +1380,19 @@ static int unixSync(sqlite3_file *id, int flags){ ** A failed directory sync is not a big deal. So it seems ** better to ignore the error. Ticket #1657 */ + /* pFile->lastErrno = errno; */ /* return SQLITE_IOERR; */ } #endif - close(pFile->dirfd); /* Only need to sync once, so close the directory */ - pFile->dirfd = -1; /* when we are done. */ + 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; + } } - return SQLITE_OK; + return rc; } /* @@ -1325,6 +1404,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ SimulateIOError( return SQLITE_IOERR_TRUNCATE ); rc = ftruncate(((unixFile*)id)->h, (off_t)nByte); if( rc ){ + ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_TRUNCATE; }else{ return SQLITE_OK; @@ -1341,6 +1421,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ rc = fstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ + ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; @@ -1808,11 +1889,22 @@ static int unixUnlock(sqlite3_file *id, int locktype){ if( pOpen->nLock==0 && pOpen->nPending>0 ){ int i; for(i=0; inPending; i++){ - close(pOpen->aPending[i]); + /* close pending fds, but if closing fails don't free the array + ** assign -1 to the successfully closed descriptors and record the + ** error. The next attempt to unlock will try again. */ + if( pOpen->aPending[i] < 0 ) continue; + if( close(pOpen->aPending[i]) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + }else{ + pOpen->aPending[i] = -1; + } + } + if( rc==SQLITE_OK ){ + sqlite3_free(pOpen->aPending); + pOpen->nPending = 0; + pOpen->aPending = 0; } - sqlite3_free(pOpen->aPending); - pOpen->nPending = 0; - pOpen->aPending = 0; } } } @@ -1833,10 +1925,20 @@ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; if( pFile ){ if( pFile->dirfd>=0 ){ - close(pFile->dirfd); + int err = close(pFile->dirfd); + if( err ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + }else{ + pFile->dirfd=-1; + } } if( pFile->h>=0 ){ - close(pFile->h); + int err = close(pFile->h); + if( err ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + } } #if IS_VXWORKS if( pFile->isDelete && pFile->zRealpath ){ @@ -1868,6 +1970,7 @@ static int closeUnixFile(sqlite3_file *id){ ** Close a file. */ static int unixClose(sqlite3_file *id){ + int rc = SQLITE_OK; if( id ){ unixFile *pFile = (unixFile *)id; unixUnlock(id, NO_LOCK); @@ -1892,25 +1995,24 @@ static int unixClose(sqlite3_file *id){ } releaseLockInfo(pFile->pLock); releaseOpenCnt(pFile->pOpen); - closeUnixFile(id); + rc = closeUnixFile(id); leaveMutex(); } - return SQLITE_OK; + return rc; } #if SQLITE_ENABLE_LOCKING_STYLE - #if !IS_VXWORKS -#pragma mark AFP Support +#pragma mark AFP support /* ** The afpLockingContext structure contains all afp lock specific state */ typedef struct afpLockingContext afpLockingContext; struct afpLockingContext { - unsigned long long sharedLockByte; - const char *filePath; + unsigned long long sharedByte; + const char *dbPath; }; struct ByteRangeLockPB2 @@ -1943,14 +2045,22 @@ static int _AFPFSSetLock( pb.offset = offset; pb.length = length; pb.fd = pFile->h; - OSTRACE5("AFPLOCK setting lock %s for %d in range %llx:%llx\n", - (setLockFlag?"ON":"OFF"), pFile->h, offset, length); + //SimulateIOErrorBenign(1); + //SimulateIOError( pb.fd=(-1) ) + //SimulateIOErrorBenign(0); + + OSTRACE6("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", + (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), offset, length); err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); if ( err==-1 ) { int rc; int tErrno = errno; - OSTRACE4("AFPLOCK failed to fsctl() '%s' %d %s\n", path, tErrno, strerror(tErrno)); - rc = sqliteErrorFromPosixError(tErrno, setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); /* error */ + OSTRACE4("AFPSETLOCK failed to fsctl() '%s' %d %s\n", path, tErrno, strerror(tErrno)); +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS + rc = SQLITE_BUSY; +#else + rc = sqliteErrorFromPosixError(tErrno, setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); +#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -1981,11 +2091,11 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){ */ if( !reserved ){ /* lock the RESERVED byte */ - int lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1); + int lrc = _AFPFSSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); if( SQLITE_OK==lrc ){ /* if we succeeded in taking the reserved lock, unlock it to restore ** the original state */ - lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1, 0); + lrc = _AFPFSSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); } else { /* if we failed to get the lock then someone else must have it */ reserved = 1; @@ -2048,7 +2158,7 @@ static int afpLock(sqlite3_file *id, int locktype){ || (locktype==EXCLUSIVE_LOCK && pFile->locktypefilePath, pFile, PENDING_BYTE, 1, 1); + failed = _AFPFSSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 1); if (failed) { rc = failed; goto afp_end_lock; @@ -2064,14 +2174,14 @@ static int afpLock(sqlite3_file *id, int locktype){ /* Now get the read-lock SHARED_LOCK */ /* note that the quality of the randomness doesn't matter that much */ lk = random(); - context->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); - lrc1 = _AFPFSSetLock(context->filePath, pFile, - SHARED_FIRST+context->sharedLockByte, 1, 1); + context->sharedByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); + lrc1 = _AFPFSSetLock(context->dbPath, pFile, + SHARED_FIRST+context->sharedByte, 1, 1); if( IS_LOCK_ERROR(lrc1) ){ lrc1Errno = pFile->lastErrno; } /* Drop the temporary PENDING lock */ - lrc2 = _AFPFSSetLock(context->filePath, pFile, PENDING_BYTE, 1, 0); + lrc2 = _AFPFSSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); if( IS_LOCK_ERROR(lrc1) ) { pFile->lastErrno = lrc1Errno; @@ -2084,6 +2194,7 @@ static int afpLock(sqlite3_file *id, int locktype){ rc = lrc1; } else { pFile->locktype = SHARED_LOCK; + pFile->pOpen->nLock++; } }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is @@ -2094,7 +2205,7 @@ static int afpLock(sqlite3_file *id, int locktype){ assert( 0!=pFile->locktype ); if (locktype >= RESERVED_LOCK && pFile->locktype < RESERVED_LOCK) { /* Acquire a RESERVED lock */ - failed = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1); + failed = _AFPFSSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); } if (!failed && locktype == EXCLUSIVE_LOCK) { /* Acquire an EXCLUSIVE lock */ @@ -2102,16 +2213,22 @@ static int afpLock(sqlite3_file *id, int locktype){ /* 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 = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST + - context->sharedLockByte, 1, 0))) { + if( !(failed = _AFPFSSetLock(context->dbPath, pFile, SHARED_FIRST + + context->sharedByte, 1, 0)) ){ + int failed2 = SQLITE_OK; /* now attemmpt to get the exclusive lock range */ - failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST, + failed = _AFPFSSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); - if (failed && (failed = _AFPFSSetLock(context->filePath, pFile, - SHARED_FIRST + context->sharedLockByte, 1, 1))) { - rc = failed; - } - } else { + if( failed && (failed2 = _AFPFSSetLock(context->dbPath, pFile, + SHARED_FIRST + context->sharedByte, 1, 1)) ){ + /* Can't reestablish the shared lock. Sqlite can't deal, this is + ** a critical I/O error + */ + rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 : + SQLITE_IOERR_LOCK; + goto afp_end_lock; + } + }else{ rc = failed; } } @@ -2143,7 +2260,7 @@ afp_end_lock: static int afpUnlock(sqlite3_file *id, int locktype) { int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; + afpLockingContext *pCtx = (afpLockingContext *) pFile->lockingContext; assert( pFile ); OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype, @@ -2157,51 +2274,55 @@ static int afpUnlock(sqlite3_file *id, int locktype) { return SQLITE_MISUSE; } enterMutex(); - int failed = SQLITE_OK; if( pFile->locktype>SHARED_LOCK ){ - if( locktype==SHARED_LOCK ){ + + if( pFile->locktype==EXCLUSIVE_LOCK ){ + rc = _AFPFSSetLock(pCtx->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); + if( rc==SQLITE_OK && locktype==SHARED_LOCK ){ + /* only re-establish the shared lock if necessary */ + int sharedLockByte = SHARED_FIRST+pCtx->sharedByte; + rc = _AFPFSSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 1); + } + } + if( rc==SQLITE_OK && pFile->locktype>=PENDING_LOCK ){ + rc = _AFPFSSetLock(pCtx->dbPath, pFile, PENDING_BYTE, 1, 0); + } + if( rc==SQLITE_OK && pFile->locktype>=RESERVED_LOCK ){ + rc = _AFPFSSetLock(pCtx->dbPath, pFile, RESERVED_BYTE, 1, 0); + } + }else if( locktype==NO_LOCK ){ + /* clear the shared lock */ + int sharedLockByte = SHARED_FIRST+pCtx->sharedByte; + rc = _AFPFSSetLock(pCtx->dbPath, pFile, sharedLockByte, 1, 0); + } - /* unlock the exclusive range - then re-establish the shared lock */ - if (pFile->locktype==EXCLUSIVE_LOCK) { - failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST, - SHARED_SIZE, 0); - if (!failed) { - /* successfully removed the exclusive lock */ - if ((failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST+ - context->sharedLockByte, 1, 1))) { - /* failed to re-establish our shared lock */ - rc = failed; + if( rc==SQLITE_OK ){ + if( locktype==NO_LOCK ){ + struct openCnt *pOpen = pFile->pOpen; + pOpen->nLock--; + assert( pOpen->nLock>=0 ); + if( pOpen->nLock==0 && pOpen->nPending>0 ){ + int i; + for(i=0; inPending; i++){ + if( pOpen->aPending[i] < 0 ) continue; + if( close(pOpen->aPending[i]) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + }else{ + pOpen->aPending[i] = -1; } - } else { - rc = failed; - } + } + if( rc==SQLITE_OK ){ + sqlite3_free(pOpen->aPending); + pOpen->nPending = 0; + pOpen->aPending = 0; + } } } - if (rc == SQLITE_OK && pFile->locktype>=PENDING_LOCK) { - if ((failed = _AFPFSSetLock(context->filePath, pFile, - PENDING_BYTE, 1, 0))){ - /* failed to release the pending lock */ - rc = failed; - } - } - if (rc == SQLITE_OK && pFile->locktype>=RESERVED_LOCK) { - if ((failed = _AFPFSSetLock(context->filePath, pFile, - RESERVED_BYTE, 1, 0))) { - /* failed to release the reserved lock */ - rc = failed; - } - } } - if( locktype==NO_LOCK ){ - int failed = _AFPFSSetLock(context->filePath, pFile, - SHARED_FIRST + context->sharedLockByte, 1, 0); - if (failed) { - rc = failed; - } - } - if (rc == SQLITE_OK) - pFile->locktype = locktype; +end_afpunlock: leaveMutex(); + if( rc==SQLITE_OK ) pFile->locktype = locktype; return rc; } @@ -2212,9 +2333,31 @@ static int afpClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; afpUnlock(id, NO_LOCK); + enterMutex(); + 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->aPending. It will be automatically closed when + ** the last lock is cleared. + */ + int *aNew; + struct openCnt *pOpen = pFile->pOpen; + aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); + if( aNew==0 ){ + /* If a malloc fails, just leak the file descriptor */ + }else{ + pOpen->aPending = aNew; + pOpen->aPending[pOpen->nPending] = pFile->h; + pOpen->nPending++; + pFile->h = -1; + } + } + releaseOpenCnt(pFile->pOpen); sqlite3_free(pFile->lockingContext); + closeUnixFile(id); + leaveMutex(); } - return closeUnixFile(id); + return SQLITE_OK; } @@ -2270,12 +2413,19 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ } OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ + rc = SQLITE_OK; + reserved=1; + } +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ *pResOut = reserved; return rc; } static int flockLock(sqlite3_file *id, int locktype) { int rc = SQLITE_OK; + int lrc; unixFile *pFile = (unixFile*)id; assert( pFile ); @@ -2302,6 +2452,11 @@ static int flockLock(sqlite3_file *id, int locktype) { } OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ + rc = SQLITE_BUSY; + } +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ return rc; } @@ -2332,6 +2487,12 @@ static int flockUnlock(sqlite3_file *id, int locktype) { if( IS_LOCK_ERROR(r) ){ pFile->lastErrno = tErrno; } +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + if( (r & SQLITE_IOERR) == SQLITE_IOERR ){ + r = SQLITE_BUSY; + } +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ + return r; } else { pFile->locktype = NO_LOCK; @@ -2352,6 +2513,7 @@ static int flockClose(sqlite3_file *id) { #endif /* !IS_VXWORKS */ #pragma mark Old-School .lock file based locking +#define DOTLOCK_SUFFIX ".lock" /* Dotlock-style reserved lock checking following the behavior of ** unixCheckReservedLock, see the unixCheckReservedLock function comments */ @@ -2427,12 +2589,15 @@ static int dotlockLock(sqlite3_file *id, int locktype) { } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + pFile->lastErrno = tErrno; } } goto dotlock_end_lock; } - close(fd); + if( close(fd) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + } /* got it, set the type and return ok */ pFile->locktype = locktype; @@ -2523,8 +2688,8 @@ static int namedsemCheckReservedLock(sqlite3_file *id, int *pResOut) { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); pFile->lastErrno = tErrno; } else { - /* someone else has the lock when we are in NO_LOCK */ - reserved = (pFile->locktype < SHARED_LOCK); + /* someone else has the lock when we are in NO_LOCK */ + reserved = (pFile->locktype < SHARED_LOCK); } }else{ /* we could have it if we want it */ @@ -2617,6 +2782,577 @@ static int namedsemClose(sqlite3_file *id) { #endif /* IS_VXWORKS */ +#pragma mark Proxy locking support + +static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) { + unixFile *pFile = (unixFile*)id; + int rc = takeConch(pFile); + if( rc==SQLITE_OK ){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + unixFile *proxy = pCtx->lockProxy; + return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut); + } + return rc; +} + +static int proxyLock(sqlite3_file *id, int locktype) { + unixFile *pFile = (unixFile*)id; + int rc = takeConch(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; + } + return rc; +} + +static int proxyUnlock(sqlite3_file *id, int locktype) { + unixFile *pFile = (unixFile*)id; + int rc = takeConch(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; + } + return rc; +} + +/* + ** Close a file. + */ +static int proxyClose(sqlite3_file *id) { + if( id ){ + unixFile *pFile = (unixFile*)id; + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + unixFile *lockProxy = pCtx->lockProxy; + unixFile *conchFile = pCtx->conchFile; + int rc = SQLITE_OK; + + if( lockProxy ){ + rc = lockProxy->pMethod->xUnlock((sqlite3_file*)lockProxy, NO_LOCK); + if( rc ) return rc; + rc = lockProxy->pMethod->xClose((sqlite3_file*)lockProxy); + if( rc ) return rc; + sqlite3_free(lockProxy); + } + if( conchFile ){ + if( pCtx->conchHeld ){ + rc = releaseConch(pFile); + if( rc ) return rc; + } + rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile); + if( rc ) return rc; + sqlite3_free(conchFile); + } + sqlite3_free(pCtx->lockProxyPath); + sqlite3_free(pCtx->conchFilePath); + sqlite3_free(pCtx->dbPath); + /* restore the original locking context and pMethod then close it */ + pFile->lockingContext = pCtx->oldLockingContext; + pFile->pMethod = pCtx->pOldMethod; + sqlite3_free(pCtx); + return pFile->pMethod->xClose(id); + } + return SQLITE_OK; +} + +/* 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 genHostID(char *pHostID){ + int pid, fd, i, 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 + /* 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; +} + +#ifdef SQLITE_TEST +/* simulate multiple hosts by creating unique hostid file paths */ +int sqlite3_hostid_num = 0; +#endif + +/* writes the host id path to path, path should be an pre-allocated buffer +** with enough space for a path */ +static int getHostIDPath(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 +*/ +static int getHostID(char *pHostID, int *pError){ + int fd; + char path[MAXPATHLEN]; + size_t len; + int rc=SQLITE_OK; + + getHostIDPath(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 ){ + int err = errno; +#ifdef SQLITE_PROXY_DEBUG /* set the sqlite error message instead */ + 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( lenlockingContext; + + if( pCtx->conchHeld>0 ){ + return SQLITE_OK; + }else{ + unixFile *conchFile = pCtx->conchFile; + char testValue[CONCHLEN]; + char conchValue[CONCHLEN]; + char lockPath[MAXPATHLEN]; + char *tLockPath = 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()); + + 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 = getHostID(testValue, &pError); + if( rc&SQLITE_IOERR==SQLITE_IOERR ){ + pFile->lastErrno = pError; + } + if( pCtx->lockProxyPath ){ + strlcpy(&testValue[HOSTIDLEN], pCtx->lockProxyPath, MAXPATHLEN); + } + } + if( rc!=SQLITE_OK ){ + goto end_takeconch; + } + + readRc = unixRead((sqlite3_file *)conchFile, conchValue, CONCHLEN, 0); + if( readRc!=SQLITE_IOERR_SHORT_READ ){ + int match = 0; + if( readRc!=SQLITE_OK ){ + if( rc&SQLITE_IOERR==SQLITE_IOERR ){ + pFile->lastErrno = conchFile->lastErrno; + } + rc = readRc; + goto end_takeconch; + } + /* 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( !memcmp(testValue, conchValue, HOSTIDLEN) ){ + tLockPath = (char *)&conchValue[HOSTIDLEN]; + goto end_takeconch; + } + }else{ + /* we've got the conch if conchValue matches our path and host ID */ + if( !memcmp(testValue, conchValue, CONCHLEN) ){ + goto end_takeconch; + } + } + }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 ){ + getLockPath(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 ){ + mode_t mode = buf.st_mode & 0100666; + /* try to match the database file permissions, ignore failure */ +#ifndef SQLITE_PROXY_DEBUG + fchmod(conchFile->h, buf.st_mode); +#else + if( fchmod(conchFile->h, buf.st_mode)!=0 ){ + 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); + } + }else{ + int code = errno; + fprintf(stderr, "STAT FAILED[%d] with %d %s\n", err, code, strerror(code)); +#endif + } + } + } + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); + +end_takeconch: + OSTRACE2("TRANSPROXY: CLOSE %d\n", pFile->h); + if( rc==SQLITE_OK && pFile->oflags ){ + if( pFile->h>=0 ){ +#ifdef STRICT_CLOSE_ERROR + if( close(pFile->h) ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + } +#else + close(pFile->h); /* silently leak fd if fail */ +#endif + } + pFile->h = -1; + int fd = open(pCtx->dbPath, pFile->oflags, SQLITE_DEFAULT_FILE_PERMISSIONS); + OSTRACE2("TRANSPROXY: OPEN %d\n", fd); + if( fd>=0 ){ + pFile->h = fd; + }else{ + rc=SQLITE_CANTOPEN; // SQLITE_BUSY? takeConch called during locking + } + } + if( rc==SQLITE_OK && !pCtx->lockProxy ){ + char *path = tLockPath ? tLockPath : pCtx->lockProxyPath; + // ACS: Need to make a copy of path sometimes + rc = createProxyUnixFile(path, &pCtx->lockProxy); + } + if( rc==SQLITE_OK ){ + pCtx->conchHeld = 1; + + if( tLockPath ){ + pCtx->lockProxyPath = sqlite3DbStrDup(0, tLockPath); + if( pCtx->lockProxy->pMethod == ioMethodForLockingStyle(LOCKING_STYLE_AFP) ){ + ((afpLockingContext *)pCtx->lockProxy->lockingContext)->dbPath = pCtx->lockProxyPath; + } + } + } else { + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); + } + OSTRACE3("TAKECONCH %d %s\n", conchFile->h, rc==SQLITE_OK ? "ok" : "failed"); + return rc; + } +} + +static int releaseConch(unixFile *pFile){ + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; + int rc; + unixFile *conchFile = pCtx->conchFile; + + OSTRACE4("RELEASECONCH %d for %s pid=%d\n", conchFile->h, + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), + getpid()); + pCtx->conchHeld = 0; + rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); + OSTRACE3("RELEASECONCH %d %s\n", conchFile->h, + (rc==SQLITE_OK ? "ok" : "failed")); + return rc; +} + +static int getConchPathFromDBPath(char *dbPath, char **pConchPath){ + int i; + int len = strlen(dbPath); + char *conchPath; + + conchPath = (char *)sqlite3_malloc(len + 8); + if( conchPath==0 ){ + return SQLITE_NOMEM; + } + strlcpy(conchPath, dbPath, len+1); + + /* now insert a "." before the last / character */ + for( i=(len-1); i>=0; i-- ){ + if( conchPath[i]=='/' ){ + i++; + break; + } + } + conchPath[i]='.'; + while ( ilockingContext; + char *oldPath = pCtx->lockProxyPath; + int taken = 0; + int rc = SQLITE_OK; + + if( pFile->locktype!=NO_LOCK ){ + return SQLITE_BUSY; + } + + /* nothing to do if the path is NULL, :auto: or matches the existing path */ + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || + (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ + return SQLITE_OK; + }else{ + unixFile *lockProxy = pCtx->lockProxy; + pCtx->lockProxy=NULL; + pCtx->conchHeld = 0; + if( lockProxy!=NULL ){ + rc=lockProxy->pMethod->xClose((sqlite3_file *)lockProxy); + if( rc ) return rc; + sqlite3_free(lockProxy); + } + sqlite3_free(oldPath); + pCtx->lockProxyPath = sqlite3DbStrDup(0, path); + } + + return rc; +} + +/* +** Takes an already filled in unix file and alters it so all file locking +** will be performed on the local proxy lock file. The following fields +** are preserved in the locking context so that they can be restored and +** the unix structure properly cleaned up at close time: +** ->lockingContext +** ->pMethod +*/ +static int transformUnixFileForLockProxy(unixFile *pFile, const char *path) { + proxyLockingContext *pCtx; + char dbPath[MAXPATHLEN]; + char *lockPath=NULL; + int rc = SQLITE_OK; + + if( pFile->locktype!=NO_LOCK ){ + return SQLITE_BUSY; + } + getDbPathForUnixFile(pFile, dbPath); + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ + lockPath=NULL; + }else{ + lockPath=(char *)path; + } + + OSTRACE4("TRANSPROXY %d for %s pid=%d\n", pFile->h, + (lockPath ? lockPath : ":auto:"), getpid()); + + pCtx = sqlite3_malloc( sizeof(*pCtx) ); + if( pCtx==0 ){ + return SQLITE_NOMEM; + } + memset(pCtx, 0, sizeof(*pCtx)); + + rc = getConchPathFromDBPath(dbPath, &pCtx->conchFilePath); + if( rc==SQLITE_OK ){ + rc = createProxyUnixFile(pCtx->conchFilePath, &pCtx->conchFile); + } + if( rc==SQLITE_OK && lockPath ){ + pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath); + } + +end_transform_file: + 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 = ioMethodForLockingStyle(LOCKING_STYLE_PROXY); + }else{ + if( pCtx->conchFile ){ + rc = pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); + if( rc ) return rc; + sqlite3_free(pCtx->conchFile); + } + sqlite3_free(pCtx->conchFilePath); + sqlite3_free(pCtx); + } + OSTRACE3("TRANSPROXY %d %s\n", pFile->h, + (rc==SQLITE_OK ? "ok" : "failed")); + return rc; +} + +static int createProxyUnixFile(const char *path, unixFile **ppFile) { + int fd; + int dirfd = -1; + unixFile *pNew; + int rc = SQLITE_OK; + + fd = open(path, O_RDWR | O_CREAT, SQLITE_DEFAULT_FILE_PERMISSIONS); + if( fd<0 ){ + return SQLITE_CANTOPEN; + } + + pNew = (unixFile *)sqlite3_malloc(sizeof(unixFile)); + if( pNew==NULL ){ + rc = SQLITE_NOMEM; + goto end_create_proxy; + } + memset(pNew, 0, sizeof(unixFile)); + + rc = fillInUnixFile(NULL, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); + if( rc==SQLITE_OK ){ + *ppFile = pNew; + return SQLITE_OK; + } +end_create_proxy: + close(fd); /* silently leak fd if error, we're already in error */ + sqlite3_free(pNew); + return rc; +} + + #endif /* SQLITE_ENABLE_LOCKING_STYLE */ /* @@ -2661,6 +3397,63 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ *(int*)pArg = ((unixFile*)id)->locktype; return SQLITE_OK; } + case SQLITE_GET_LOCKPROXYFILE: { +#if SQLITE_ENABLE_LOCKING_STYLE + unixFile *pFile = (unixFile*)id; + if( pFile->pMethod == ioMethodForLockingStyle(LOCKING_STYLE_PROXY) ){ + proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; + takeConch(pFile); + if( pCtx->lockProxyPath ){ + *(const char **)pArg = pCtx->lockProxyPath; + }else{ + *(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)); + if( pArg==NULL || (const char *)pArg==0 ){ + if( isProxyStyle ){ + // turn off proxy locking - not supported + rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; + }else{ + // 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)) ){ + rc = SQLITE_OK; + }else{ + rc = switchLockProxyPath(pFile, proxyPath); + } + }else{ + // 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; + } + } return SQLITE_ERROR; } @@ -2688,6 +3481,43 @@ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ return 0; } +#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) +#if SQLITE_ENABLE_LOCKING_STYLE +,IOMETHODS(dotlockClose, dotlockLock, dotlockUnlock,dotlockCheckReservedLock) +#if IS_VXWORKS + ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) + ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) + ,IOMETHODS(namedsemClose, namedsemLock, namedsemUnlock, namedsemCheckReservedLock) + ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) +#else + ,IOMETHODS(flockClose, flockLock, flockUnlock, flockCheckReservedLock) + ,IOMETHODS(afpClose, afpLock, afpUnlock, afpCheckReservedLock) + ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) + ,IOMETHODS(proxyClose, proxyLock, proxyUnlock, proxyCheckReservedLock) +#endif +#endif +/* The order of the IOMETHODS macros above is important. It must be the + ** same order as the LOCKING_STYLE numbers + */ +}; + /* ** Initialize the contents of the unixFile structure pointed to by pId. ** @@ -2709,52 +3539,6 @@ static int fillInUnixFile( unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; - /* Macro to define the static contents of an sqlite3_io_methods - ** structure for a unix backend file. Different locking methods - ** require different functions for the xClose, xLock, xUnlock and - ** xCheckReservedLock methods. - */ - #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) -#if SQLITE_ENABLE_LOCKING_STYLE - ,IOMETHODS(dotlockClose, dotlockLock, dotlockUnlock,dotlockCheckReservedLock) -#if IS_VXWORKS - ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) - ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) - ,IOMETHODS(namedsemClose, namedsemLock, namedsemUnlock, namedsemCheckReservedLock) -#else - ,IOMETHODS(flockClose, flockLock, flockUnlock, flockCheckReservedLock) - ,IOMETHODS(afpClose, afpLock, afpUnlock, afpCheckReservedLock) - ,IOMETHODS(nolockClose, nolockLock, nolockUnlock, nolockCheckReservedLock) -#endif -#endif - }; - /* The order of the IOMETHODS macros above is important. It must be the - ** same order as the LOCKING_STYLE numbers - */ - assert(LOCKING_STYLE_POSIX==1); - assert(LOCKING_STYLE_NONE==2); - assert(LOCKING_STYLE_DOTFILE==3); - assert(LOCKING_STYLE_FLOCK==4); - assert(LOCKING_STYLE_AFP==5); - assert(LOCKING_STYLE_NAMEDSEM==6); - assert( pNew->pLock==NULL ); assert( pNew->pOpen==NULL ); @@ -2811,16 +3595,35 @@ static int fillInUnixFile( eLockingStyle = LOCKING_STYLE_NONE; }else{ eLockingStyle = detectLockingStyle(pVfs, 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 } + /* Macro to define the static contents of an sqlite3_io_methods + ** structure for a unix backend file. Different locking methods + ** require different functions for the xClose, xLock, xUnlock and + ** xCheckReservedLock methods. + */ + assert(LOCKING_STYLE_POSIX==1); + assert(LOCKING_STYLE_NONE==2); + assert(LOCKING_STYLE_DOTFILE==3); + assert(LOCKING_STYLE_FLOCK==4); + assert(LOCKING_STYLE_AFP==5); + assert(LOCKING_STYLE_NAMEDSEM==6); + assert(LOCKING_STYLE_PROXY==7); + switch( eLockingStyle ){ case LOCKING_STYLE_POSIX: { enterMutex(); #if IS_VXWORKS - rc = findLockInfo(h, pNew->zRealpath, &pNew->pLock, &pNew->pOpen); + rc = findLockInfo(pNew, pNew->zRealpath, &pNew->pLock, &pNew->pOpen); #else - rc = findLockInfo(h, &pNew->pLock, &pNew->pOpen); + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); #endif leaveMutex(); break; @@ -2841,8 +3644,11 @@ static int fillInUnixFile( /* 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->filePath = zFilename; + pCtx->dbPath = zFilename; srandomdev(); + enterMutex(); + rc = findLockInfo(pNew, NULL, &pNew->pOpen); + leaveMutex(); } break; } @@ -2859,7 +3665,7 @@ static int fillInUnixFile( if( zLockFile==0 ){ rc = SQLITE_NOMEM; }else{ - sqlite3_snprintf(nFilename, zLockFile, "%s.lock", zFilename); + sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); } pNew->lockingContext = zLockFile; break; @@ -2868,8 +3674,8 @@ static int fillInUnixFile( #if IS_VXWORKS case LOCKING_STYLE_NAMEDSEM: { /* Named semaphore locking uses the file path so it needs to be - ** included in the namedsemLockingContext - */ + ** included in the namedsemLockingContext + */ enterMutex(); rc = findLockInfo(h, pNew->zRealpath, &pNew->pLock, &pNew->pOpen); if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ @@ -2904,7 +3710,7 @@ static int fillInUnixFile( pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); + if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ close(h); }else{ pNew->pMethod = &aIoMethod[eLockingStyle-1]; @@ -2913,6 +3719,29 @@ static int fillInUnixFile( return rc; } +#if SQLITE_ENABLE_LOCKING_STYLE +static sqlite3_io_methods *ioMethodForLockingStyle(int style){ + return &aIoMethod[style-1]; +} + +static int getDbPathForUnixFile(unixFile *pFile, char *dbPath){ + if( pFile->pMethod==ioMethodForLockingStyle(LOCKING_STYLE_AFP) ){ + /* 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) ){ + /* 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; + } + /* all other styles use the locking context to store the db file path */ + strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); + return SQLITE_OK; +} +#endif + /* ** Open a file descriptor to the directory containing file zFilename. ** If successful, *pFd is set to the opened file descriptor and @@ -2951,6 +3780,7 @@ static int openDirectory(const char *zFilename, int *pFd){ */ static int getTempname(int nBuf, char *zBuf){ static const char *azDirs[] = { + 0, 0, "/var/tmp", "/usr/tmp", @@ -2972,7 +3802,11 @@ static int getTempname(int nBuf, char *zBuf){ SimulateIOError( return SQLITE_IOERR ); azDirs[0] = sqlite3_temp_directory; - for(i=0; ioflags = oflags; } if( pOutFlags ){ *pOutFlags = flags; @@ -3124,9 +3960,9 @@ static int unixOpen( assert(fd!=0); if( isOpenDirectory ){ - int rc = openDirectory(zPath, &dirfd); + rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ - close(fd); + close(fd); /* silently leak if fail, already in error */ return rc; } } @@ -3136,6 +3972,38 @@ static int unixOpen( #endif noLock = eType!=SQLITE_OPEN_MAIN_DB; + +#if SQLITE_PREFER_PROXY_LOCKING + 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 + */ + if( envforce!=NULL ){ + useProxy = atoi(envforce)>0; + }else{ + struct statfs fsInfo; + + if( statfs(zPath, &fsInfo) == -1 ){ + ((unixFile*)pFile)->lastErrno = errno; + if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ + close(fd); /* silently leak if fail, in error */ + return SQLITE_IOERR_ACCESS; + } + useProxy = !(fsInfo.f_flags&MNT_LOCAL); + } + if( useProxy ){ + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + if( rc==SQLITE_OK ){ + rc = transformUnixFileForLockProxy((unixFile*)pFile, ":auto:"); + } + return rc; + } + } +#endif + return fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); } @@ -3161,7 +4029,9 @@ static int unixDelete(sqlite3_vfs *NotUsed, const char *zPath, int dirSync){ { rc = SQLITE_IOERR_DIR_FSYNC; } - close(fd); + if( close(fd)&&!rc ){ + rc = SQLITE_IOERR_DIR_CLOSE; + } } } #endif @@ -3282,7 +4152,7 @@ static int unixFullPathname( zFull[j] = 0; } #endif -#endif +#endif /* #if IS_VXWORKS */ } @@ -3482,6 +4352,7 @@ int sqlite3_os_init(void){ UNIXVFS("unix-dotfile", LOCKING_STYLE_DOTFILE), UNIXVFS("unix-none", LOCKING_STYLE_NONE), UNIXVFS("unix-namedsem",LOCKING_STYLE_NAMEDSEM), + UNIXVFS("unix-proxy", LOCKING_STYLE_PROXY) }; for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], 0); diff --git a/src/pragma.c b/src/pragma.c index 71464e1d8e..a467b62897 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.194 2008/11/17 19:18:55 danielk1977 Exp $ +** $Id: pragma.c,v 1.195 2008/11/21 00:10:35 aswift Exp $ */ #include "sqliteInt.h" #include @@ -706,6 +706,48 @@ void sqlite3Pragma( } }else + /* + ** PRAGMA [database.]lock_proxy_file + ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path" + ** + ** Return or set the value of the lock_proxy_file flag. Changing + ** the value sets a specific file to be used for database access locks. + ** + */ + if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){ + if( !zRight ){ + Pager *pPager = sqlite3BtreePager(pDb->pBt); + char *proxy_file_path = NULL; + sqlite3_file *pFile = sqlite3PagerFile(pPager); + sqlite3OsFileControl(pFile, SQLITE_GET_LOCKPROXYFILE, + &proxy_file_path); + + if( proxy_file_path ){ + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, + "lock_proxy_file", SQLITE_STATIC); + sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); + } + }else{ + Pager *pPager = sqlite3BtreePager(pDb->pBt); + sqlite3_file *pFile = sqlite3PagerFile(pPager); + int res; + if( zRight[0] ){ + res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, + zRight); + } else { + res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE, + NULL); + } + if( res!=SQLITE_OK ){ + sqlite3ErrorMsg(pParse, "failed to set lock proxy file"); + goto pragma_out; + } + } + }else + + /* ** PRAGMA [database.]synchronous ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 8f1c734595..e98cf03080 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.415 2008/11/19 01:20:26 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.416 2008/11/21 00:10:35 aswift Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -507,6 +507,8 @@ int sqlite3_exec( #define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) /* ** CAPI3REF: Flags For File Open Operations {H10230} @@ -723,6 +725,9 @@ struct sqlite3_io_methods { ** is defined. */ #define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_GET_LOCKPROXYFILE 2 +#define SQLITE_SET_LOCKPROXYFILE 3 +#define SQLITE_LAST_ERRNO 4 /* ** CAPI3REF: Mutex Handle {H17110} diff --git a/src/test1.c b/src/test1.c index 4c502564b5..62aa0e11b7 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.329 2008/10/30 15:03:16 drh Exp $ +** $Id: test1.c,v 1.330 2008/11/21 00:10:35 aswift Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -4466,9 +4466,87 @@ static int file_control_test( assert( rc==SQLITE_ERROR ); rc = sqlite3_file_control(db, "temp", -1, &iArg); assert( rc==SQLITE_ERROR ); + + return TCL_OK; +} + + +/* +** tclcmd: file_control_lasterrno_test DB +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_LAST_ERRNO verb. +*/ +static int file_control_lasterrno_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iArg = 0; + sqlite3 *db; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetStringFromObj(objv[0], 0), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + rc = sqlite3_file_control(db, NULL, SQLITE_LAST_ERRNO, &iArg); + if( rc ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_ERROR; } + if( iArg!=0 ) { + Tcl_AppendResult(interp, "Unexpected non-zero errno: ", + Tcl_GetStringFromObj(Tcl_NewIntObj(iArg), 0), " ", 0); + return TCL_ERROR; + } return TCL_OK; } +/* +** tclcmd: file_control_lockproxy_test DB +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and +** SQLITE_SET_LOCKPROXYFILE verbs. +*/ +static int file_control_lockproxy_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iArg = 0; + sqlite3 *db; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + 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 + { + 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_GET_LOCKPROXYFILE, &testPath); + if( strncmp(proxyPath,testPath,11) ) { + 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; } + 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; +} + + /* ** tclcmd: sqlite3_vfs_list ** @@ -4640,6 +4718,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_open_file_count; extern int sqlite3_sort_count; extern int sqlite3_current_time; + extern int sqlite3_hostid_num; extern int sqlite3_max_blobsize; extern int sqlite3BtreeSharedCacheReport(void*, Tcl_Interp*,int,Tcl_Obj*CONST*); @@ -4784,6 +4863,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "vfs_unregister_all", vfs_unregister_all, 0 }, { "vfs_reregister_all", vfs_reregister_all, 0 }, { "file_control_test", file_control_test, 0 }, + { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, + { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, /* Functions from os.h */ @@ -4855,6 +4936,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ (char*)&sqlite3_open_file_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_current_time", (char*)&sqlite3_current_time, TCL_LINK_INT); + Tcl_LinkVar(interp, "sqlite_hostid_num", + (char*)&sqlite3_hostid_num, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite3_xferopt_count", (char*)&sqlite3_xferopt_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite3_pager_readdb_count", diff --git a/src/test_config.c b/src/test_config.c index 2bbb3b6f1b..a5b847a9da 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -16,7 +16,7 @@ ** The focus of this file is providing the TCL testing layer ** access to compile-time constants. ** -** $Id: test_config.c,v 1.42 2008/10/12 00:27:54 shane Exp $ +** $Id: test_config.c,v 1.43 2008/11/21 00:10:35 aswift Exp $ */ #include "sqliteLimit.h" @@ -392,6 +392,13 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double", 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); +#else + 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); #else diff --git a/test/exclusive.test b/test/exclusive.test index 91253d4afc..f60de3eb79 100644 --- a/test/exclusive.test +++ b/test/exclusive.test @@ -12,7 +12,7 @@ # of these tests is exclusive access mode (i.e. the thing activated by # "PRAGMA locking_mode = EXCLUSIVE"). # -# $Id: exclusive.test,v 1.9 2008/09/24 14:03:43 danielk1977 Exp $ +# $Id: exclusive.test,v 1.10 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -402,6 +402,17 @@ do_test exclusive-4.5 { db close sqlite db test.db +# if we're using proxy locks, we use 3 filedescriptors for a db +# that is open but NOT writing changes, normally +# sqlite uses 1 (proxy locking adds the conch and the local lock) +set using_proxy 0 +foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set using_proxy $value +} +set extrafds 0 +if {$using_proxy!=0} { + set extrafds 2 +} do_test exclusive-5.0 { execsql { @@ -414,6 +425,7 @@ do_test exclusive-5.0 { do_test exclusive-5.1 { # Three files are open: The db, journal and statement-journal. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {3} do_test exclusive-5.2 { execsql { @@ -421,6 +433,7 @@ do_test exclusive-5.2 { } # One file open: the db. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {1} do_test exclusive-5.3 { execsql { @@ -430,6 +443,7 @@ do_test exclusive-5.3 { } # Two files open: the db and journal. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {2} do_test exclusive-5.4 { execsql { @@ -437,6 +451,7 @@ do_test exclusive-5.4 { } # Three files are open: The db, journal and statement-journal. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {3} do_test exclusive-5.5 { execsql { @@ -444,6 +459,7 @@ do_test exclusive-5.5 { } # Three files are still open: The db, journal and statement-journal. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {3} do_test exclusive-5.6 { execsql { @@ -454,6 +470,7 @@ do_test exclusive-5.6 { do_test exclusive-5.7 { # Just the db open. set sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds } {1} finish_test diff --git a/test/filectrl.test b/test/filectrl.test index eb0a0e461d..fe89a62635 100644 --- a/test/filectrl.test +++ b/test/filectrl.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: filectrl.test,v 1.1 2008/01/22 14:50:17 drh Exp $ +# $Id: filectrl.test,v 1.2 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -27,5 +27,15 @@ do_test filectrl-1.3 { sqlite3 db :memory: file_control_test db } {} - +do_test filectrl-1.4 { + sqlite3 db test.db + file_control_lasterrno_test db +} {} +do_test filectrl-1.5 { + db close + sqlite3 db test_control_lockproxy.db + file_control_lockproxy_test db +} {} +db close +file delete -force .test_control_lockproxy.db-conch test.proxy finish_test diff --git a/test/lock5.test b/test/lock5.test index 0f0b5d2f2a..4c740a32c4 100644 --- a/test/lock5.test +++ b/test/lock5.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock5.test,v 1.3 2008/09/24 09:12:47 danielk1977 Exp $ +# $Id: lock5.test,v 1.4 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -25,6 +25,16 @@ if {[catch {sqlite3 db test.db -vfs unix-none} msg]} { } db close +ifcapable lock_proxy_pragmas { + set ::using_proxy 0 + foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set ::using_proxy $value + } + # Disable the proxy locking for these tests + set env(SQLITE_FORCE_PROXY_LOCKING) "0" +} + + do_test lock5-dotfile.1 { sqlite3 db test.db -vfs unix-dotfile execsql { @@ -173,4 +183,8 @@ do_test lock5-flock.X { db2 close } {} +ifcapable lock_proxy_pragmas { + set env(SQLITE_FORCE_PROXY_LOCKING) $::using_proxy +} + finish_test diff --git a/test/lock6.test b/test/lock6.test new file mode 100644 index 0000000000..23d32ebd62 --- /dev/null +++ b/test/lock6.test @@ -0,0 +1,168 @@ +# 2008 October 6 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is database locks. +# +# $Id: lock6.test,v 1.1 2008/11/21 00:10:35 aswift Exp $ + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Launch another testfixture process to be controlled by this one. A +# channel name is returned that may be passed as the first argument to proc +# 'testfixture' to execute a command. The child testfixture process is shut +# down by closing the channel. +proc launch_testfixture {} { + set prg [info nameofexec] + if {$prg eq ""} { + set prg [file join . testfixture] + } + set chan [open "|$prg tf_main2.tcl" r+] + fconfigure $chan -buffering line + return $chan +} + +# Execute a command in a child testfixture process, connected by two-way +# channel $chan. Return the result of the command, or an error message. +proc testfixture {chan cmd} { + puts $chan $cmd + puts $chan OVER + set r "" + while { 1 } { + set line [gets $chan] + if { $line == "OVER" } { + return $r + } + append r $line + } +} + +# Write the main loop for the child testfixture processes into file +# tf_main2.tcl. The parent (this script) interacts with the child processes +# via a two way pipe. The parent writes a script to the stdin of the child +# process, followed by the word "OVER" on a line of its own. The child +# process evaluates the script and writes the results to stdout, followed +# by an "OVER" of its own. +set f [open tf_main2.tcl w] +puts $f { + set l [open log w] + set script "" + while {![eof stdin]} { + flush stdout + set line [gets stdin] + puts $l "READ $line" + if { $line == "OVER" } { + catch {eval $script} result + puts $result + puts $l "WRITE $result" + puts OVER + puts $l "WRITE OVER" + flush stdout + set script "" + } else { + append script $line + append script " ; " + } + } + close $l +} +close $f + + +ifcapable lock_proxy_pragmas { + set sqlite_hostid_num 1 + + set using_proxy 0 + foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set using_proxy $value + } + + # Test the lock_proxy_file pragmas. + # + set env(SQLITE_FORCE_PROXY_LOCKING) "1" + + do_test lock6-1.1 { + set ::tf1 [launch_testfixture] + testfixture $::tf1 "set sqlite_pending_byte $::sqlite_pending_byte" + testfixture $::tf1 { + set sqlite_hostid_num 2 + sqlite3 db test.db -key xyzzy + set lockpath [db eval { + PRAGMA lock_proxy_file=":auto:"; + select * from sqlite_master; + PRAGMA lock_proxy_file; + }] + string match "*test.db:auto:" $lockpath + } + } {1} + + set sqlite_hostid_num 3 + do_test lock6-1.2 { + execsql {pragma lock_status} + } {main unlocked temp closed} + + sqlite3_soft_heap_limit 0 + do_test lock6-1.3 { + sqlite3 db test.db + catchsql { + select * from sqlite_master; + } + } {1 {database is locked}} + + do_test lock6-1.4 { + set lockpath [execsql { + PRAGMA lock_proxy_file=":auto:"; + PRAGMA lock_proxy_file; + } db] + set lockpath + } {{:auto: (not held)}} + + do_test lock6-1.4.1 { + catchsql { + PRAGMA lock_proxy_file="notmine"; + select * from sqlite_master; + } db + } {1 {database is locked}} + + do_test lock6-1.4.2 { + execsql { + PRAGMA lock_proxy_file; + } db + } {notmine} + + do_test lock6-1.5 { + testfixture $::tf1 { + db eval { + BEGIN; + SELECT * FROM sqlite_master; + } + } + } {} + + catch {testfixture $::tf1 {db close}} + + do_test lock6-1.6 { + execsql { + PRAGMA lock_proxy_file="mine"; + select * from sqlite_master; + } db + } {} + + catch {close $::tf1} + set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy + set sqlite_hostid_num 0 + + sqlite3_soft_heap_limit $soft_limit + +} + +finish_test diff --git a/test/manydb.test b/test/manydb.test index 9af5465b54..b9a8eaf8d4 100644 --- a/test/manydb.test +++ b/test/manydb.test @@ -13,12 +13,23 @@ # This file implements tests the ability of the library to open # many different databases at the same time without leaking memory. # -# $Id: manydb.test,v 1.3 2006/01/11 01:08:34 drh Exp $ +# $Id: manydb.test,v 1.4 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl set N 300 +# if we're using proxy locks, we use 5 filedescriptors for a db +# that is open and in the middle of writing changes, normally +# sqlite uses 3 (proxy locking adds the conch and the local lock) +set using_proxy 0 +foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set using_proxy value +} +set num_fd_per_openwrite_db 3 +if {$using_proxy>0} { + set num_fd_per_openwrite_db 5 +} # First test how many file descriptors are available for use. To open a # database for writing SQLite requires 3 file descriptors (the database, the @@ -35,7 +46,7 @@ foreach fd $filehandles { catch { file delete -force testfile.1 } -set N [expr $i / 3] +set N [expr $i / $num_fd_per_openwrite_db] # Create a bunch of random database names # diff --git a/test/pragma.test b/test/pragma.test index 348d17cac2..2cfc961a70 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -12,7 +12,7 @@ # # This file implements tests for the PRAGMA command. # -# $Id: pragma.test,v 1.69 2008/10/23 05:45:07 danielk1977 Exp $ +# $Id: pragma.test,v 1.70 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -34,6 +34,7 @@ source $testdir/tester.tcl # pragma-14.*: Test the page_count pragma. # pragma-15.*: Test that the value set using the cache_size pragma is not # reset when the schema is reloaded. +# pragma-16.*: Test proxy locking # ifcapable !pragma { @@ -1252,4 +1253,134 @@ sqlite3 dbX :memory: dbX eval {PRAGMA temp_store_directory = ""} dbX close +ifcapable lock_proxy_pragmas { + set sqlite_hostid_num 1 + + set using_proxy 0 + foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set using_proxy $value + } + + # Test the lock_proxy_file pragmas. + # + db close + set env(SQLITE_FORCE_PROXY_LOCKING) "0" + + sqlite3 db test.db + do_test pragma-16.1 { + execsql { + PRAGMA lock_proxy_file="mylittleproxy"; + select * from sqlite_master; + } + execsql { + PRAGMA lock_proxy_file; + } + } {mylittleproxy} + + do_test pragma-16.2 { + sqlite3 db2 test.db + execsql { + PRAGMA lock_proxy_file="mylittleproxy"; + } db2 + } {} + + db2 close + do_test pragma-16.2.1 { + sqlite3 db2 test.db + execsql { + PRAGMA lock_proxy_file=":auto:"; + select * from sqlite_master; + } db2 + execsql { + PRAGMA lock_proxy_file; + } db2 + } {mylittleproxy} + + db2 close + do_test pragma-16.3 { + sqlite3 db2 test.db + execsql { + PRAGMA lock_proxy_file="myotherproxy"; + } db2 + catchsql { + select * from sqlite_master; + } db2 + } {1 {database is locked}} + + do_test pragma-16.4 { + db2 close + db close + sqlite3 db2 test.db + execsql { + PRAGMA lock_proxy_file="myoriginalproxy"; + PRAGMA lock_proxy_file="myotherproxy"; + PRAGMA lock_proxy_file; + } db2 + } {myotherproxy} + + db2 close + set env(SQLITE_FORCE_PROXY_LOCKING) "1" + do_test pragma-16.5 { + sqlite3 db2 test.db + execsql { + PRAGMA lock_proxy_file=":auto:"; + PRAGMA lock_proxy_file; + } db2 + } {myotherproxy} + + do_test pragma-16.6 { + db2 close + sqlite3 db2 test2.db + set lockpath [execsql { + PRAGMA lock_proxy_file=":auto:"; + PRAGMA lock_proxy_file; + } db2] + string match "*test2.db:auto:" $lockpath + } {1} + + set sqlite_hostid_num 2 + do_test pragma-16.7 { + sqlite3 db test2.db + execsql { + PRAGMA lock_proxy_file=":auto:"; + } + catchsql { + select * from sqlite_master; + } + } {1 {database is locked}} + db close + + do_test pragma-16.8 { + sqlite3 db test2.db + catchsql { + select * from sqlite_master; + } + } {1 {database is locked}} + + db2 close + do_test pragma-16.8.1 { + execsql { + PRAGMA lock_proxy_file="yetanotherproxy"; + PRAGMA lock_proxy_file; + } + } {yetanotherproxy} + do_test pragma-16.8.2 { + execsql { + create table mine(x); + } + } {} + + db close + do_test pragma-16.9 { + sqlite3 db proxytest.db + set lockpath2 [execsql { + PRAGMA lock_proxy_file=":auto:"; + PRAGMA lock_proxy_file; + } db] + string match "*proxytest.db:auto:" $lockpath2 + } {1} + + set env(SQLITE_FORCE_PROXY_LOCKING) $using_proxy + set sqlite_hostid_num 0 +} finish_test diff --git a/test/shared.test b/test/shared.test index 61a28c49b5..d57f54e739 100644 --- a/test/shared.test +++ b/test/shared.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: shared.test,v 1.34 2008/07/12 14:52:20 drh Exp $ +# $Id: shared.test,v 1.35 2008/11/21 00:10:35 aswift Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -42,6 +42,20 @@ ifcapable autovacuum { } } +# if we're using proxy locks, we use 2 filedescriptors for a db +# that is open but NOT yet locked, after a lock is taken we'll have 3, +# normally sqlite uses 1 (proxy locking adds the conch and the local lock) +set using_proxy 0 +foreach {name value} [array get env SQLITE_FORCE_PROXY_LOCKING] { + set using_proxy $value +} +set extrafds_prelock 0 +set extrafds_postlock 0 +if {$using_proxy>0} { + set extrafds_prelock 1 + set extrafds_postlock 2 +} + # $av is currently 0 if this loop iteration is to test with auto-vacuum turned # off, and 1 if it is turned on. Increment it so that (1 -> no auto-vacuum) # and (2 -> auto-vacuum). The sole reason for this is so that it looks nicer @@ -74,6 +88,7 @@ do_test shared-$av.1.1 { # opened. sqlite3 db2 test.db set ::sqlite_open_file_count + expr $sqlite_open_file_count-$extrafds_postlock } {1} do_test shared-$av.1.2 { # Add a table and a single row of data via the first connection. @@ -154,6 +169,7 @@ do_test shared-$av.2.1 { sqlite3 db3 TEST.DB } set ::sqlite_open_file_count + expr $sqlite_open_file_count-($extrafds_prelock+$extrafds_postlock) } {2} do_test shared-$av.2.2 { # Start read transactions on db and db2 (the shared pager cache). Ensure @@ -284,14 +300,17 @@ sqlite3 db test.db sqlite3 db2 test2.db do_test shared-$av.4.1.1 { set sqlite_open_file_count + expr $sqlite_open_file_count-($extrafds_prelock*2) } {2} do_test shared-$av.4.1.2 { execsql {ATTACH 'test2.db' AS test2} set sqlite_open_file_count + expr $sqlite_open_file_count-($extrafds_postlock*2) } {2} do_test shared-$av.4.1.3 { execsql {ATTACH 'test.db' AS test} db2 set sqlite_open_file_count + expr $sqlite_open_file_count-($extrafds_postlock*2) } {2} # Sanity check: Create a table in ./test.db via handle db, and test that handle diff --git a/test/tester.tcl b/test/tester.tcl index 5a42196fd8..e6530a3316 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -11,7 +11,7 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.134 2008/08/05 17:53:24 drh Exp $ +# $Id: tester.tcl,v 1.135 2008/11/21 00:10:35 aswift Exp $ # # What for user input before continuing. This gives an opportunity @@ -659,6 +659,7 @@ proc do_ioerr_test {testname args} { do_test $testname.$n.1 { set ::sqlite_io_error_pending 0 catch {db close} + catch {db2 close} catch {file delete -force test.db} catch {file delete -force test.db-journal} catch {file delete -force test2.db}