diff -r -N -u sqlite-3.3.6/Makefile.in sqlite-3.3.6-osx/Makefile.in --- sqlite-3.3.6/Makefile.in 2006-06-06 11:52:26.000000000 +0100 +++ sqlite-3.3.6-osx/Makefile.in 2006-07-09 19:12:01.000000000 +0100 @@ -62,6 +62,9 @@ # TCC += -DSQLITE_THREAD_OVERRIDE_LOCK=@THREADSOVERRIDELOCKS@ +# Should we enable locking callbacks +TCC += -DENABLE_LOCKING_CALLBACKS=1 + # The fdatasync library TLIBS = @TARGET_LIBS@ @@ -130,7 +133,7 @@ select.lo table.lo tokenize.lo trigger.lo update.lo \ util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbefifo.lo vdbemem.lo \ - where.lo utf.lo legacy.lo + where.lo utf.lo legacy.lo NSSQLiteLockingSupport.lo # All of the source code files. # @@ -183,7 +186,8 @@ $(TOP)/src/vdbefifo.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbeInt.h \ - $(TOP)/src/where.c + $(TOP)/src/where.c \ + $(TOP)/src/NSSQLiteLockingSupport.c # Source code to the test files. # @@ -211,7 +215,8 @@ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbe.c \ - $(TOP)/src/where.c + $(TOP)/src/where.c \ + $(TOP)/src/NSSQLiteLockingSupport.c # Header files used by all library source files. # @@ -224,7 +229,8 @@ $(TOP)/src/os_common.h \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/vdbe.h \ - parse.h + parse.h \ + $(TOP)/src/NSSQLiteLockingSupport.h # Header files used by the VDBE submodule # @@ -351,6 +357,9 @@ os_unix.lo: $(TOP)/src/os_unix.c $(HDR) $(LTCOMPILE) -c $(TOP)/src/os_unix.c +NSSQLiteLockingSupport.lo: $(TOP)/src/NSSQLiteLockingSupport.c $(HDR) + $(LTCOMPILE) -c $(TOP)/src/NSSQLiteLockingSupport.c + os_win.lo: $(TOP)/src/os_win.c $(HDR) $(LTCOMPILE) -c $(TOP)/src/os_win.c diff -r -N -u sqlite-3.3.6/src/NSSQLiteLockingSupport.c sqlite-3.3.6-osx/src/NSSQLiteLockingSupport.c --- sqlite-3.3.6/src/NSSQLiteLockingSupport.c 1970-01-01 01:00:00.000000000 +0100 +++ sqlite-3.3.6-osx/src/NSSQLiteLockingSupport.c 2006-07-09 19:17:02.000000000 +0100 @@ -0,0 +1,536 @@ +/* + * All copyright in this software has been dedicated to the public domain. + * + * This software is provided on an "AS IS" basis. THE AUTHORS AND PROVIDERS OF + * THIS SOFTWARE MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT + * LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS SOFTWARE OR ITS USE AND + * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL THE AUTHORS OR PROVIDERS OF THIS SOFTWARE BE LIABLE FOR + * ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE + * USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THIS SOFTWARE, + * HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING + * NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF THE AUTHORS AND/OR + * PROVIDERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma mark #includes + +#include +#include + +#include "NSSQLiteLockingSupport.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#pragma mark AFP Support + +struct ByteRangeLockPB2 +{ + unsigned long long offset; /* offset to first byte to lock */ + unsigned long long length; /* nbr of bytes to lock */ + unsigned long long retRangeStart; /* nbr of first byte locked if successful */ + unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ + unsigned char startEndFlag; /* 1 = rel to end of fork, 0 = rel to start */ + int fd; /* file descriptor to assoc this lock with */ +}; + +#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) + +typedef struct AFPLockingContext { + unsigned long long sharedLockByte; + int currentLockType; +} AFPLockingContext; + +// return 0 on success +// return 1 on failure +static int _AFPFSSetLock(const char *path, int fd, unsigned long long offset, unsigned long long length, int setLockFlag) +{ + struct ByteRangeLockPB2 pb; + int err; + + pb.unLockFlag = setLockFlag ? 0 : 1; + pb.startEndFlag = 0; + pb.offset = offset; + pb.length = length; + pb.fd = fd; + err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); + if ( err==-1 ) { + //! fprintf(stdout, "Failed to fsctl() '%s' %d %s\n", id->filePath, errno, strerror(errno)); + return 1; // error + } else { + return 0; + } +} + +#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */ +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +// return 0 if no lock held +// return 1 if lock is held +static int _afpCheckReservedLock(const char *path, int pathFD, SQLite3OsLockingCallbacks *lockingState) { + AFPLockingContext *lockingContext = (AFPLockingContext *) lockingState->context; + + if (lockingContext->currentLockType == RESERVED_LOCK) { + return 1; // already have a reserved lock + } else { + int rc = _AFPFSSetLock(path, pathFD, RESERVED_BYTE, 1, 1); // lock the byte + if (!rc) + _AFPFSSetLock(path, pathFD, RESERVED_BYTE, 1, 0); // if it succeeded, unlock it + return rc; // if we could get the lock, we don't have the lock. Got it? + } +} + +static int _afpLock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) +{ + AFPLockingContext *lockingContext = (AFPLockingContext *) lockingState->context; + int rc = SQLITE_OK; + int lockAttemptFailure = 0; + int newLocktype; + int gotPendingLock = 0; + + if( lockingContext->currentLockType >= locktype ){ + return SQLITE_OK; + } + + /* Make sure the locking sequence is correct + */ + assert( lockingContext->currentLockType!=NO_LOCK || locktype==SHARED_LOCK ); + assert( locktype!=PENDING_LOCK ); + assert( locktype!=RESERVED_LOCK || lockingContext->currentLockType==SHARED_LOCK ); + + /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or + ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = lockingContext->currentLockType; + if( lockingContext->currentLockType==NO_LOCK + || (locktype==EXCLUSIVE_LOCK && lockingContext->currentLockType==RESERVED_LOCK) ){ + lockAttemptFailure = _AFPFSSetLock(path, pathFD, PENDING_BYTE, 1, 1); + gotPendingLock = !lockAttemptFailure; + } + + /* Acquire a shared lock + */ + if( locktype==SHARED_LOCK && (!lockAttemptFailure) ){ + assert( lockingContext->currentLockType==NO_LOCK ); + int lk = rand(); // note that the quality of the randomness doesn't matter that much + lockingContext->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); + lockAttemptFailure = _AFPFSSetLock(path, pathFD, SHARED_FIRST+lockingContext->sharedLockByte, 1, 1); + if( !lockAttemptFailure ){ + newLocktype = SHARED_LOCK; + } + } + + /* Acquire a RESERVED lock + */ + if( locktype==RESERVED_LOCK && (!lockAttemptFailure) ){ + assert( lockingContext->currentLockType==SHARED_LOCK ); + lockAttemptFailure = _AFPFSSetLock(path, pathFD, RESERVED_BYTE, 1, 1); + if( !lockAttemptFailure ){ + newLocktype = RESERVED_LOCK; + } + } + + /* Acquire a PENDING lock + */ + if( locktype==EXCLUSIVE_LOCK && (!lockAttemptFailure) ){ + newLocktype = PENDING_LOCK; + gotPendingLock = 0; + } + + /* Acquire an EXCLUSIVE lock + */ + if( locktype==EXCLUSIVE_LOCK && (!lockAttemptFailure) ){ + assert( lockingContext->currentLockType>=SHARED_LOCK ); + lockAttemptFailure = _AFPFSSetLock(path, pathFD, SHARED_FIRST + lockingContext->sharedLockByte, 1, 0); + lockAttemptFailure = _AFPFSSetLock(path, pathFD, SHARED_FIRST, SHARED_SIZE, 1); + if( (!lockAttemptFailure) ){ + newLocktype = EXCLUSIVE_LOCK; + } + } + + /* If we are holding a PENDING lock that ought to be released, then + ** release it now. + */ + if( gotPendingLock && locktype==SHARED_LOCK ){ + _AFPFSSetLock(path, pathFD, PENDING_BYTE, 1, 0); + } + + /* Update the state of the lock has held in the file descriptor then + ** return the appropriate result code. + */ + if( (!lockAttemptFailure) ){ + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } + lockingContext->currentLockType = newLocktype; + return rc; +} + +static int _afpUnlock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) { + AFPLockingContext *lockingContext = (AFPLockingContext *) lockingState->context; + int type; + assert( locktype<=SHARED_LOCK ); + type = lockingContext->currentLockType; + if( type>=EXCLUSIVE_LOCK ){ + _AFPFSSetLock(path, pathFD, SHARED_FIRST, SHARED_SIZE, 0); + } + if( type>=RESERVED_LOCK ){ + _AFPFSSetLock(path, pathFD, RESERVED_BYTE, 1, 0); + } + if( locktype==NO_LOCK && type>=SHARED_LOCK && typesharedLockByte, 1, 0); + } + if( type>=PENDING_LOCK ){ + _AFPFSSetLock(path, pathFD, PENDING_BYTE, 1, 0); + } + lockingContext->currentLockType = locktype; + return SQLITE_OK; +} + +static void _freeAFPCallbackStruct(void *callbackStruct) { + SQLite3OsLockingCallbacks *_callbackStruct = (SQLite3OsLockingCallbacks *) callbackStruct; + free((void *)(_callbackStruct->cachedPath)); + free(_callbackStruct->context); + free(_callbackStruct); +} + +#pragma mark flock() style locking +typedef struct FlockLockingContext { + int currentLockType; +} FlockLockingContext; + +static void _freeFlockCallbackStruct(void *callbackStruct) { + SQLite3OsLockingCallbacks *_callbackStruct = (SQLite3OsLockingCallbacks *) callbackStruct; + free((void *)(_callbackStruct->cachedPath)); + free(_callbackStruct->context); + free(_callbackStruct); +} + +static int _flockCheckReservedLock(const char *path, int pathFD, SQLite3OsLockingCallbacks *lockingState) { + FlockLockingContext *lockingContext = (FlockLockingContext *) lockingState->context; + + if (lockingContext->currentLockType == RESERVED_LOCK) { + return 1; // already have a reserved lock + } else { + // attempt to get the lock + int rc = flock(pathFD, LOCK_EX | LOCK_NB); + if (!rc) { + // got the lock, unlock it + flock(pathFD, LOCK_UN); + return 0; // no one has it reserved + } + return 1; // someone else might have it reserved + } +} + +static int _flockLock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) { + FlockLockingContext *lockingContext = (FlockLockingContext *) lockingState->context; + + // if we already have a lock, it is exclusive. Just adjust level and punt on outta here. + if (lockingContext->currentLockType > NO_LOCK) { + lockingContext->currentLockType = locktype; + return SQLITE_OK; + } + + // grab an exclusive lock + int rc = flock(pathFD, LOCK_EX | LOCK_NB); + if (rc) { + // didn't get, must be busy + return SQLITE_BUSY; + } else { + // got it, set the type and return ok + lockingContext->currentLockType = locktype; + return SQLITE_OK; + } +} + +static int _flockUnlock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) { + FlockLockingContext *lockingContext = (FlockLockingContext *) lockingState->context; + + assert( locktype<=SHARED_LOCK ); + + // no-op if possible + if( lockingContext->currentLockType==locktype ){ + return SQLITE_OK; + } + + // shared can just be set because we always have an exclusive + if (locktype==SHARED_LOCK) { + lockingContext->currentLockType = locktype; + return SQLITE_OK; + } + + // no, really, unlock. + int rc = flock(pathFD, LOCK_UN); + if (rc) + return SQLITE_IOERR; + else { + lockingContext->currentLockType = NO_LOCK; + return SQLITE_OK; + } +} + +#pragma mark Old-School .lock file based locking +typedef struct _LegacyLockingStateContext { + char *lockpath; + int debug; + int currentLockType; +} LegacyLockingStateContext; + +static void _freeLegacyLockingCallbackStructure(void *callbackStruct) { + SQLite3OsLockingCallbacks *_callbackStruct = (SQLite3OsLockingCallbacks *) callbackStruct; + LegacyLockingStateContext *_context = _callbackStruct->context; + free((void *)(_callbackStruct->cachedPath)); + free(_context->lockpath); + free(_context); + free(_callbackStruct); +} + +static int _legacyCheckReservedLock(const char *path, int pathFD, SQLite3OsLockingCallbacks *lockingState) { + LegacyLockingStateContext *lockingContext = (LegacyLockingStateContext *) lockingState->context; + + if (lockingContext->currentLockType == RESERVED_LOCK) { + return 1; // already have a reserved lock + } else { + struct stat statBuf; + if (lstat(lockingContext->lockpath,&statBuf) == 0) + // file exists, someone else has the lock + return 1; + else + // file does not exist, we could have it if we want it + return 0; + } +} + +static int _legacyLock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) { + LegacyLockingStateContext *lockingContext = (LegacyLockingStateContext *) lockingState->context; + + // if we already have a lock, it is exclusive. Just adjust level and punt on outta here. + if (lockingContext->currentLockType > NO_LOCK) { + lockingContext->currentLockType = locktype; + + /* Always update the timestamp on the old file */ + utimes(lockingContext->lockpath,NULL); + return SQLITE_OK; + } + + // check to see if lock file already exists + struct stat statBuf; + if (lstat(lockingContext->lockpath,&statBuf) == 0){ + return SQLITE_BUSY; // it does, busy + } + + // grab an exclusive lock + int fd = open(lockingContext->lockpath,O_RDONLY|O_CREAT|O_EXCL,0600); + if (fd < 0) + return SQLITE_BUSY; // failed to open/create the file, someone else may have stolen the lock + close(fd); + + // got it, set the type and return ok + lockingContext->currentLockType = locktype; + return SQLITE_OK; +} + +static int _legacyUnlock(const char *path, int pathFD, int locktype, SQLite3OsLockingCallbacks *lockingState) { + LegacyLockingStateContext *lockingContext = (LegacyLockingStateContext *) lockingState->context; + + assert( locktype<=SHARED_LOCK ); + + // no-op if possible + if( lockingContext->currentLockType==locktype ){ + return SQLITE_OK; + } + + // shared can just be set because we always have an exclusive + if (locktype==SHARED_LOCK) { + lockingContext->currentLockType = locktype; + return SQLITE_OK; + } + + // no, really, unlock. + unlink(lockingContext->lockpath); + lockingContext->currentLockType = NO_LOCK; + return SQLITE_OK; +} + +#pragma mark Callback structure instance constructors + +SQLite3OsLockingCallbacks *_NSNewAFPSQLiteLockingCallbacks(const char *path, int fd) { + SQLite3OsLockingCallbacks *_afpLockingCallbacks; + + _afpLockingCallbacks = calloc(1, sizeof(SQLite3OsLockingCallbacks)); + + _afpLockingCallbacks->checkReservedFuncPtr = (sqlite3OsCheckReservedLockPathCallback)_afpCheckReservedLock; + _afpLockingCallbacks->lockFuncPtr = (sqlite3OsLockPathCallback)_afpLock; + _afpLockingCallbacks->unlockFuncPtr = (sqlite3OsUnlockPathCallback)_afpUnlock; + + _afpLockingCallbacks->cachedPath = strdup(path); + _afpLockingCallbacks->cachedFD = fd; + _afpLockingCallbacks->context = calloc(1, sizeof(AFPLockingContext)); + _afpLockingCallbacks->free = _freeAFPCallbackStruct; + + return _afpLockingCallbacks; +} + +SQLite3OsLockingCallbacks *_NSNewLegacySQLiteLockingCallbacks(const char *path, int fd) { + SQLite3OsLockingCallbacks *_legacyLockingCallbacks; + + _legacyLockingCallbacks = calloc(1, sizeof(SQLite3OsLockingCallbacks)); + + _legacyLockingCallbacks->checkReservedFuncPtr = (sqlite3OsCheckReservedLockPathCallback)_legacyCheckReservedLock; + _legacyLockingCallbacks->lockFuncPtr = (sqlite3OsLockPathCallback)_legacyLock; + _legacyLockingCallbacks->unlockFuncPtr = (sqlite3OsUnlockPathCallback)_legacyUnlock; + + _legacyLockingCallbacks->cachedPath = strdup(path); + _legacyLockingCallbacks->cachedFD = fd; + _legacyLockingCallbacks->context = calloc(1, sizeof(LegacyLockingStateContext)); + _legacyLockingCallbacks->free = _freeLegacyLockingCallbackStructure; + + LegacyLockingStateContext *lockingContext = (LegacyLockingStateContext *) _legacyLockingCallbacks->context; + + lockingContext->debug = 0; + + lockingContext->lockpath = malloc(strlen(path) + strlen(".lock") + 1); + sprintf(lockingContext->lockpath,"%s.lock",path); + + return _legacyLockingCallbacks; +} + +SQLite3OsLockingCallbacks *_NSNewFlockLockingCallbacks(const char *path, int fd) { + SQLite3OsLockingCallbacks *_flockLockingCallbacks; + + _flockLockingCallbacks = calloc(1, sizeof(SQLite3OsLockingCallbacks)); + + _flockLockingCallbacks->checkReservedFuncPtr = (sqlite3OsCheckReservedLockPathCallback)_flockCheckReservedLock; + _flockLockingCallbacks->lockFuncPtr = (sqlite3OsLockPathCallback)_flockLock; + _flockLockingCallbacks->unlockFuncPtr = (sqlite3OsUnlockPathCallback)_flockUnlock; + + _flockLockingCallbacks->cachedPath = strdup(path); + _flockLockingCallbacks->cachedFD = fd; + _flockLockingCallbacks->context = calloc(1, sizeof(FlockLockingContext)); + _flockLockingCallbacks->free = _freeFlockCallbackStruct; + + return _flockLockingCallbacks; +} + +const static SQLite3OsLockingCallbacks _passThroughToSQliteCallbacks __attribute__ ((nocommon)) = { NULL, NULL, NULL, NULL, 0, NULL, NULL }; + +SQLite3OsLockingCallbacks *_NSNewUseInternalSQLiteLockingCallbacks(const char *path, int fd) { + // plug in lock stealer fnction here as context(?) + return (SQLite3OsLockingCallbacks *) &_passThroughToSQliteCallbacks; +} + +const static SQLite3OsLockingCallbacks _noOpLockingCallbacks __attribute__ ((nocommon)) = { NULL, NULL, NULL, NULL, 0, NULL, NULL }; + +SQLite3OsLockingCallbacks *_NSNewNoOpSQLiteLockingCallbacks(const char *path, int fd) { + //! might need to change this to have actual no-op functions instead of falling through + return (SQLite3OsLockingCallbacks *) &_noOpLockingCallbacks; +} + +#pragma mark File system detection & selection routines + +static _NSFSType _fsAtPathShouldActLike(const char *path, int fd) { + // test fcntl + struct flock lockInfo; + + lockInfo.l_len = 1; + lockInfo.l_start = 0; + lockInfo.l_whence = SEEK_SET; + lockInfo.l_type = F_RDLCK; + + if (fcntl(fd, F_GETLK, (int) &lockInfo) != -1) { + return _NSAdvisoryLocksWorkFS; + } + + // test flock + /* + OK -- this proves to be a lose. Specifically, NFS on 10.2.8 will return "yeah, sure" for this, even though flock() calls that try to set an actual lock will fail miserably with ENOTSUP. While we could try to set and remove a lock, we will be screwed if something else has already set the lock. And, really, unlocking isn't a very good test anyway because someone else may have locked the file and the unlock will fail for a perfectly valid reason. So, flock() is out entirely. + if (flock(fd, LOCK_UN | LOCK_NB) != -1) { + fprintf(stdout, "Apparently, flock works on %s\n", path); + return _NSFlockLocksWorkFS; + }*/ + + // fall back to legacy + return _NSDotLockFilesFS; +} + +_NSFSType _NSFSTypeAtPath(const char *path, int fd) { + struct statfs fsInfo; + + if (statfs(path, &fsInfo) == -1) + return _fsAtPathShouldActLike(path, fd); + + if (fsInfo.f_flags & MNT_RDONLY) + return _NSReadOnlyFS; + + if(!strcmp(fsInfo.f_fstypename, "hfs")) + return _NSHFS; + if(!strcmp(fsInfo.f_fstypename, "afpfs")) + return _NSAFPFS; + if(!strcmp(fsInfo.f_fstypename, "nfs")) { + return _fsAtPathShouldActLike(path, fd); + } + if(!strcmp(fsInfo.f_fstypename, "ufs")) + return _NSUFS; + if(!strcmp(fsInfo.f_fstypename, "smbfs")) + return _NSSMBFS; + if(!strcmp(fsInfo.f_fstypename, "msdos")) + return _NSMSDOSFS; + if(!strcmp(fsInfo.f_fstypename, "webdav")) + return _NSWebDavFS; + + return _fsAtPathShouldActLike(path, fd); +} + +SQLite3OsLockingCallbacks *_NSSQLiteLockingCallbacksForFile(const char *path, int fd) { + if (!path) + return _NSNewUseInternalSQLiteLockingCallbacks(path, fd); + + switch (_NSFSTypeAtPath(path, fd)) { + case _NSHFS: + case _NSUFS: + case _NSNFSFS: + case _NSAdvisoryLocksWorkFS: + return _NSNewUseInternalSQLiteLockingCallbacks(path, fd); + break; + case _NSAFPFS: + return _NSNewAFPSQLiteLockingCallbacks(path, fd); + break; + case _NSMSDOSFS: // need NTFS, too? + case _NSLegacyNFSFS: + case _NSDotLockFilesFS: + return _NSNewLegacySQLiteLockingCallbacks(path, fd); + case _NSSMBFS: + case _NSFlockLocksWorkFS: + return _NSNewFlockLockingCallbacks(path, fd); + break; + case _NSReadOnlyFS: // generic for indicating that the filesystem is read-only, so locking is moot */ + return _NSNewNoOpSQLiteLockingCallbacks(path, fd); + case _NSWebDavFS: + case _NSUnknownFS: // *punt* WebDAV/iDisk might fall into this. Might. This is a very hard issue. + return NULL; + } + return NULL; +} diff -r -N -u sqlite-3.3.6/src/NSSQLiteLockingSupport.h sqlite-3.3.6-osx/src/NSSQLiteLockingSupport.h --- sqlite-3.3.6/src/NSSQLiteLockingSupport.h 1970-01-01 01:00:00.000000000 +0100 +++ sqlite-3.3.6-osx/src/NSSQLiteLockingSupport.h 2006-07-09 18:50:12.000000000 +0100 @@ -0,0 +1,56 @@ +/* + * All copyright in this software has been dedicated to the public domain. + * + * This software is provided on an "AS IS" basis. THE AUTHORS AND PROVIDERS OF + * THIS SOFTWARE MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT + * LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE, REGARDING THIS SOFTWARE OR ITS USE AND + * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL THE AUTHORS OR PROVIDERS OF THIS SOFTWARE BE LIABLE FOR + * ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE + * USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THIS SOFTWARE, + * HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING + * NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF THE AUTHORS AND/OR + * PROVIDERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Typical usage from + #import + ... + sqlite3OsSetLockingCallback(_NSSQLiteLockingCallbacksForFile); + + Note that SQLite uses a mutex such that only one thread can be calling into these functions at a time. +*/ + +/* this will enable locking callbacks when sqlite3.h is imported */ +#define ENABLE_LOCKING_CALLBACKS 1 +#include + +typedef enum { + _NSHFS, + _NSUFS, + _NSMSDOSFS, // need NTFS, too? + _NSAFPFS, + _NSNFSFS, + _NSLegacyNFSFS, + _NSSMBFS, + _NSReadOnlyFS, // generic for indicating that the filesystem is read-only, so locking is moot + _NSWebDavFS, + _NSUnknownFS, // *punt* WebDAV/iDisk might fall into this. Might. This is a very hard issue. + _NSAdvisoryLocksWorkFS, + _NSFlockLocksWorkFS, + _NSDotLockFilesFS +} _NSFSType; + +_NSFSType _NSFSTypeAtPath(const char *path, int fd); + +SQLite3OsLockingCallbacks *_NSNewUseInternalSQLiteLockingCallbacks(const char *path, int fd); +SQLite3OsLockingCallbacks *_NSNewAFPSQLiteLockingCallbacks(const char *path, int fd); +SQLite3OsLockingCallbacks *_NSNewLegacySQLiteLockingCallbacks(const char *path, int fd); +SQLite3OsLockingCallbacks *_NSNewNoOpSQLiteLockingCallbacks(const char *path, int fd); +SQLite3OsLockingCallbacks *_NSNewFlockLockingCallbacks(const char *path, int fd); + +SQLite3OsLockingCallbacks *_NSSQLiteLockingCallbacksForFile(const char *path, int fd); diff -r -N -u sqlite-3.3.6/src/main.c sqlite-3.3.6-osx/src/main.c --- sqlite-3.3.6/src/main.c 2006-06-06 11:52:26.000000000 +0100 +++ sqlite-3.3.6-osx/src/main.c 2006-07-09 18:50:53.000000000 +0100 @@ -20,6 +20,10 @@ #include "os.h" #include +#ifdef ENABLE_LOCKING_CALLBACKS +#import "NSSQLiteLockingSupport.h" +#endif + /* ** The following constant value is used by the SQLITE_BIGENDIAN and ** SQLITE_LITTLEENDIAN macros. @@ -806,6 +810,14 @@ assert( !sqlite3MallocFailed() ); +#ifdef ENABLE_LOCKING_CALLBACKS + static char haveInstalledCallbacks = 0; + if (!haveInstalledCallbacks) { + sqlite3OsSetLockingCallback(_NSSQLiteLockingCallbacksForFile); + haveInstalledCallbacks = 1; + } +#endif + /* Allocate the sqlite data structure */ db = sqliteMalloc( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; diff -r -N -u sqlite-3.3.6/src/os_unix.c sqlite-3.3.6-osx/src/os_unix.c --- sqlite-3.3.6/src/os_unix.c 2006-06-06 11:43:29.000000000 +0100 +++ sqlite-3.3.6-osx/src/os_unix.c 2006-07-10 12:53:34.000000000 +0100 @@ -73,6 +73,10 @@ typedef struct unixFile unixFile; struct unixFile { IoMethod const *pMethod; /* Always the first entry */ +#ifdef ENABLE_LOCKING_CALLBACKS + Pager *pPager; + SQLite3OsLockingCallbacks *lockingCallbacks; +#endif struct openCnt *pOpen; /* Info about all open fd's on this inode */ struct lockInfo *pLock; /* Info about locks on this inode */ int h; /* The file descriptor */ @@ -178,6 +182,46 @@ # define CHECK_THREADID(X) 0 #endif +#ifdef ENABLE_LOCKING_CALLBACKS +/* +** Code to support externalized locking API. +*/ + +// these are defined in pager.c +extern const char *_sqlite3_pager_file_path(struct Pager *pPager); //! caller must free() return value + +static SQLite3OsGetLockingCallbacksCallback _sqlite3OsGetLockingCallbacksCallback = NULL; + +void sqlite3OsSetLockingCallback(SQLite3OsGetLockingCallbacksCallback lockingCallbackFuncPtr){ + _sqlite3OsGetLockingCallbacksCallback = lockingCallbackFuncPtr; +} + +SQLite3OsGetLockingCallbacksCallback sqlite3OsGetLockingCallback() { + return _sqlite3OsGetLockingCallbacksCallback; +} + +void sqlite3OsUnixSetPager (OsFile *id, Pager *pager) +{ + if (id) { + unixFile *pFile = (unixFile *)id; + + pFile->pPager = pager; + } +} + +static void _setLockingCallbacks(unixFile *pFile) { + const char *path = _sqlite3_pager_file_path(pFile->pPager); + if (!path) { + pFile->lockingCallbacks = NULL; + } else { + pFile->lockingCallbacks + = _sqlite3OsGetLockingCallbacksCallback(path, pFile->h); + sqliteFree((char *)path); + assert( pFile->lockingCallbacks ); + } +} +#endif + /* ** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996) ** section 6.5.2.2 lines 483 through 490 specify that when a process @@ -1187,6 +1231,27 @@ unixFile *pFile = (unixFile*)id; assert( pFile ); + +#ifdef ENABLE_LOCKING_CALLBACKS + { + SQLite3OsLockingCallbacks *lockingCallbacks = pFile->lockingCallbacks; + + if (!lockingCallbacks && _sqlite3OsGetLockingCallbacksCallback) { + _setLockingCallbacks(pFile); + lockingCallbacks = pFile->lockingCallbacks; + } + + if (lockingCallbacks && lockingCallbacks->checkReservedFuncPtr) { + TRACE2("EXT TEST RESERVED %d\n", pFile->h); + sqlite3OsEnterMutex(); + r = lockingCallbacks->checkReservedFuncPtr(lockingCallbacks->cachedPath, + pFile->h, lockingCallbacks); + sqlite3OsLeaveMutex(); + return r; + } + } +#endif + sqlite3OsEnterMutex(); /* Because pFile->pLock is shared across threads */ /* Check if a thread in this process holds such a lock */ @@ -1284,6 +1349,36 @@ int s; assert( pFile ); + +#ifdef ENABLE_LOCKING_CALLBACKS + { + SQLite3OsLockingCallbacks *lockingCallbacks = pFile->lockingCallbacks; + + if (!lockingCallbacks && _sqlite3OsGetLockingCallbacksCallback) { + _setLockingCallbacks(pFile); + lockingCallbacks = pFile->lockingCallbacks; + } + + if (lockingCallbacks && lockingCallbacks->lockFuncPtr) { + TRACE7("EXT LOCK %d %s was %s(%s,%d) ppFile=%d\n", pFile->h, locktypeName(locktype), + locktypeName(pFile->locktype), locktypeName(pLock->locktype), pLock->cnt + ,getpid() ); + + sqlite3OsEnterMutex(); + int status = lockingCallbacks->lockFuncPtr(lockingCallbacks->cachedPath, + pFile->h, locktype, lockingCallbacks); + sqlite3OsLeaveMutex(); + + if (status == SQLITE_OK) { + pFile->locktype = locktype; + } else if (locktype == EXCLUSIVE_LOCK) { + pFile->locktype = PENDING_LOCK; + } + return status; + } + } +#endif + TRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", pFile->h, locktypeName(locktype), locktypeName(pFile->locktype), locktypeName(pLock->locktype), pLock->cnt , getpid()); @@ -1447,6 +1542,32 @@ unixFile *pFile = (unixFile*)id; assert( pFile ); + +#ifdef ENABLE_LOCKING_CALLBACKS + { + SQLite3OsLockingCallbacks *lockingCallbacks = pFile->lockingCallbacks; + + if (!lockingCallbacks && _sqlite3OsGetLockingCallbacksCallback) { + _setLockingCallbacks(pFile); + lockingCallbacks = pFile->lockingCallbacks; + } + + if (lockingCallbacks && lockingCallbacks->unlockFuncPtr) { + TRACE7("EXT UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype, + pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid()); + sqlite3OsEnterMutex(); + int status = lockingCallbacks->unlockFuncPtr(lockingCallbacks->cachedPath, + pFile->h, locktype, + lockingCallbacks); + sqlite3OsLeaveMutex(); + if (status == SQLITE_OK) { + pFile->locktype = locktype; + } + return status; + } + } +#endif + TRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype, pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid()); @@ -1671,6 +1792,10 @@ pInit->fullSync = 0; pInit->locktype = 0; pInit->offset = 0; +#ifdef ENABLE_LOCKING_CALLBACKS + pInit->pPager = NULL; + pInit->lockingCallbacks = NULL; +#endif SET_THREADID(pInit); pNew = sqlite3ThreadSafeMalloc( sizeof(unixFile) ); if( pNew==0 ){ diff -r -N -u sqlite-3.3.6/src/pager.c sqlite-3.3.6-osx/src/pager.c --- sqlite-3.3.6/src/pager.c 2006-06-06 11:44:03.000000000 +0100 +++ sqlite-3.3.6-osx/src/pager.c 2006-07-09 19:04:42.000000000 +0100 @@ -26,6 +26,9 @@ #include "pager.h" #include #include +#ifdef ENABLE_LOCKING_CALLBACKS +#include +#endif /* ** Macros for troubleshooting. Normally turned off @@ -1666,6 +1669,13 @@ sqliteFree(zFullPathname); strcpy(&pPager->zJournal[nameLen], "-journal"); pPager->fd = fd; +#ifdef ENABLE_LOCKING_CALLBACKS + { + extern void sqlite3OsUnixSetPager (OsFile *, Pager *); + sqlite3OsUnixSetPager (fd, pPager); + } +#endif + /* pPager->journalOpen = 0; */ pPager->useJournal = useJournal && !memDb; pPager->noReadlock = noReadlock && readOnly; @@ -3850,4 +3860,25 @@ } #endif +#ifdef ENABLE_LOCKING_CALLBACKS +const char *_sqlite3_pager_file_path(Pager *pPager) //! caller must free() return value +{ + if (!pPager) return NULL; + if (pPager->zFilename && pPager->zDirectory) { + char path[MAXPATHLEN]; + char *target; + target = stpcpy(path, pPager->zDirectory); + *target = '/'; + target++; + *target = '\0'; + target++; + target = sqliteMalloc( (target - path) + strlen(pPager->zFilename) + 1); + strcpy(target, pPager->zFilename); + return target; + } + + return NULL; +} +#endif + #endif /* SQLITE_OMIT_DISKIO */ diff -r -N -u sqlite-3.3.6/src/sqlite.h.in sqlite-3.3.6-osx/src/sqlite.h.in --- sqlite-3.3.6/src/sqlite.h.in 2006-05-02 12:51:17.000000000 +0100 +++ sqlite-3.3.6-osx/src/sqlite.h.in 2006-07-09 19:22:21.000000000 +0100 @@ -1476,6 +1476,53 @@ # undef double #endif +#ifdef ENABLE_LOCKING_CALLBACKS +// This is duplicated from os.h. There should only be one. +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +struct SQLite3OsLockingCallbacks; + +typedef int (*sqlite3OsCheckReservedLockPathCallback)(const char *path, int pathFD, struct SQLite3OsLockingCallbacks *context); +typedef int (*sqlite3OsLockPathCallback)(const char *path, int pathFD, int locktype, struct SQLite3OsLockingCallbacks *context); +typedef int (*sqlite3OsUnlockPathCallback)(const char *path, int pathFD, int locktype, struct SQLite3OsLockingCallbacks *context); + +typedef struct SQLite3OsLockingCallbacks { + sqlite3OsCheckReservedLockPathCallback checkReservedFuncPtr; + sqlite3OsLockPathCallback lockFuncPtr; + sqlite3OsUnlockPathCallback unlockFuncPtr; + const char *cachedPath; + int cachedFD; + void *context; + void (*free)(void *_callbackStruct); +} SQLite3OsLockingCallbacks; + +typedef SQLite3OsLockingCallbacks *(*SQLite3OsGetLockingCallbacksCallback)(const char *path, int pathFD); + +void sqlite3OsSetLockingCallback(SQLite3OsGetLockingCallbacksCallback lockingCallbackFuncPtr); +SQLite3OsGetLockingCallbacksCallback sqlite3OsGetLockingCallback(); +#endif + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif