Added SQLITE_IOERR_LOCK extended error code and support for detecting and returning errors in the os_unix lock, unlock and check reserved lock variants, also added support for populating and returning system error codes so that they can be accessed via xGetLastError, unfortunately xGetLastError can't seem to access the unixFile structure where the lastErrno is recorded. (CVS 5586)

FossilOrigin-Name: c1af14e2b6bf5af0aff3df3adbe8cb9aabe1c4a3
This commit is contained in:
aswift 2008-08-22 00:22:35 +00:00
parent 777c5386c3
commit 5b1a256663
4 changed files with 356 additions and 133 deletions

View File

@ -1,5 +1,5 @@
C Move\sdate+time\sfunctions\sto\sstart-time\sinitialization.\s\sAdditional\nstart-time\sfunction\scleanup.\s(CVS\s5585)
D 2008-08-21T20:21:35
C Added\sSQLITE_IOERR_LOCK\sextended\serror\scode\sand\ssupport\sfor\sdetecting\sand\sreturning\serrors\sin\sthe\sos_unix\slock,\sunlock\sand\scheck\sreserved\slock\svariants,\salso\sadded\ssupport\sfor\spopulating\sand\sreturning\ssystem\serror\scodes\sso\sthat\sthey\scan\sbe\saccessed\svia\sxGetLastError,\sunfortunately\sxGetLastError\scan't\sseem\sto\saccess\sthe\sunixFile\sstructure\swhere\sthe\slastErrno\sis\srecorded.\s(CVS\s5586)
D 2008-08-22T00:22:35
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in 51b727303f84cf055e29514d8248e5eaf9701379
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@ -133,7 +133,7 @@ F src/os.c 939ae7690a01d9401685ba124b4ba45fd4a7a2ad
F src/os.h ef8abeb9afc694b82dbd169a91c9b7e26db3c892
F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
F src/os_os2.c 676ed273b17bd260f905df81375c9f9950d85517
F src/os_unix.c fe0dbc35bcd3de49e46b132abfc0f45d6dd6a864
F src/os_unix.c 9fddf8735f77fec2fdecc09481f4241710dd8c2d
F src/os_win.c aefe9ee26430678a19a058a874e4e2bd91398142
F src/pager.c 3a4358c72c9c8415e8648001c776857e6952e2b4
F src/pager.h 3778bea71dfb9658b6c94394e18db4a5b27e6ded
@ -147,7 +147,7 @@ F src/random.c 5c754319d38abdd6acd74601ee0105504adc508a
F src/resolve.c e688f240bdacf4003047c2b023c3a4ee3a3eca98
F src/select.c e71462393fe0f9d2bf41378763b96659e8780e43
F src/shell.c d83b578a8ccdd3e0e7fef4388a0887ce9f810967
F src/sqlite.h.in 54e51c22e2294c5989156b0aec87aa44168ac1f0
F src/sqlite.h.in 2373d1d70664f7fcd78e79af3c51792bb0a0753e
F src/sqlite3ext.h 1e3887c9bd3ae66cb599e922824b04cd0d0f2c3e
F src/sqliteInt.h ddf6c9eb90b295bcb7c600139227e5e2a16c8063
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
@ -622,7 +622,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
P 30077ece459b515338723ca0dab11c18b9f09d21
R e5f18970c2a651995955578eea70a847
U drh
Z 9729df78d7652ca44708388f5fcbb37d
P 80d6a31cb3851704c09ac9d99fe4bc241df3c180
R 7431cc71998a6f7b8eb2579a2fd9f22d
U aswift
Z 6c65e4f8868dab6fff3ca3acb52662f8

View File

@ -1 +1 @@
80d6a31cb3851704c09ac9d99fe4bc241df3c180
c1af14e2b6bf5af0aff3df3adbe8cb9aabe1c4a3

View File

@ -12,7 +12,7 @@
**
** This file contains code that is specific to Unix systems.
**
** $Id: os_unix.c,v 1.195 2008/07/30 17:28:04 drh Exp $
** $Id: os_unix.c,v 1.196 2008/08/22 00:22:35 aswift Exp $
*/
#include "sqliteInt.h"
#if SQLITE_OS_UNIX /* This file is used on unix only */
@ -113,6 +113,7 @@ struct unixFile {
#if SQLITE_THREADSAFE
pthread_t tid; /* The thread that "owns" this unixFile */
#endif
int lastErrno; /* The unix errno from the last I/O error */
};
/*
@ -370,6 +371,12 @@ static struct openCnt *openList = 0;
#define LOCKING_STYLE_FLOCK 4
#define LOCKING_STYLE_AFP 5
/*
** Only set the lastErrno if the error code is a real error and not
** a normal expected return code of SQLITE_BUSY or SQLITE_OK
*/
#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))
/*
** Helper functions to obtain and relinquish the global mutex.
*/
@ -616,7 +623,11 @@ static int detectLockingStyle(
{ "hfs", LOCKING_STYLE_POSIX },
{ "ufs", LOCKING_STYLE_POSIX },
{ "afpfs", LOCKING_STYLE_AFP },
#ifdef SQLITE_ENABLE_AFP_LOCKING_SMB
{ "smbfs", LOCKING_STYLE_AFP },
#else
{ "smbfs", LOCKING_STYLE_FLOCK },
#endif
{ "msdos", LOCKING_STYLE_DOTFILE },
{ "webdav", LOCKING_STYLE_NONE },
{ 0, 0 }
@ -1123,14 +1134,76 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
return SQLITE_OK;
}
/*
** This routine translates a standard POSIX errno code into something
** useful to the clients of the sqlite3 functions. Specifically, it is
** intended to translate a variety of "try again" errors into SQLITE_BUSY
** and a variety of "please close the file descriptor NOW" errors into
** SQLITE_IOERR
**
** Errors during initialization of locks, or file system support for locks,
** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
*/
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
switch (posixError) {
case 0:
return SQLITE_OK;
case EAGAIN:
case ETIMEDOUT:
case EBUSY:
case EINTR:
case ENOLCK:
/* random NFS retry error, unless during file system support
* introspection, in which it actually means what it says */
return SQLITE_BUSY;
case EACCES:
/* EACCES is like EAGAIN during locking operations, but not any other time*/
if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
(sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
(sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
(sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
return SQLITE_BUSY;
}
/* else fall through */
case EPERM:
return SQLITE_PERM;
case EDEADLK:
return SQLITE_IOERR_BLOCKED;
case EOPNOTSUPP:
/* something went terribly awry, unless during file system support
* introspection, in which it actually means what it says */
case ENOTSUP:
/* invalid fd, unless during file system support introspection, in which
* it actually means what it says */
case EIO:
case EBADF:
case EINVAL:
case ENOTCONN:
case ENODEV:
case ENXIO:
case ENOENT:
case ESTALE:
case ENOSYS:
/* these should force the client to close the file and reconnect */
default:
return sqliteIOErr;
}
}
/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero. If the file is unlocked or holds only SHARED locks, then
** return zero.
** file by this or any other process. If such a lock is held, set *pResOut
** to a non-zero value otherwise *pResOut is set to zero. The return value
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
int r = 0;
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
@ -1140,28 +1213,31 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
/* Check if a thread in this process holds such a lock */
if( pFile->pLock->locktype>SHARED_LOCK ){
r = 1;
reserved = 1;
}
/* Otherwise see if some other process holds it.
*/
if( !r ){
if( !reserved ){
struct flock lock;
lock.l_whence = SEEK_SET;
lock.l_start = RESERVED_BYTE;
lock.l_len = 1;
lock.l_type = F_WRLCK;
fcntl(pFile->h, F_GETLK, &lock);
if( lock.l_type!=F_UNLCK ){
r = 1;
if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
pFile->lastErrno = tErrno;
} else if( lock.l_type!=F_UNLCK ){
reserved = 1;
}
}
leaveMutex();
OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
*pResOut = r;
return SQLITE_OK;
*pResOut = reserved;
return rc;
}
/*
@ -1307,7 +1383,11 @@ static int unixLock(sqlite3_file *id, int locktype){
lock.l_start = PENDING_BYTE;
s = fcntl(pFile->h, F_SETLK, &lock);
if( s==(-1) ){
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
goto end_lock;
}
}
@ -1317,24 +1397,36 @@ static int unixLock(sqlite3_file *id, int locktype){
** operating system calls for the specified lock.
*/
if( locktype==SHARED_LOCK ){
int tErrno = 0;
assert( pLock->cnt==0 );
assert( pLock->locktype==0 );
/* Now get the read-lock */
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
s = fcntl(pFile->h, F_SETLK, &lock);
if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
tErrno = errno;
}
/* Drop the temporary PENDING lock */
lock.l_start = PENDING_BYTE;
lock.l_len = 1L;
lock.l_type = F_UNLCK;
if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
goto end_lock;
if( s != -1 ){
/* This could happen with a network mount */
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
goto end_lock;
}
}
if( s==(-1) ){
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
}else{
pFile->locktype = SHARED_LOCK;
pFile->pOpen->nLock++;
@ -1364,7 +1456,11 @@ static int unixLock(sqlite3_file *id, int locktype){
}
s = fcntl(pFile->h, F_SETLK, &lock);
if( s==(-1) ){
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
}
}
@ -1423,7 +1519,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
if( fcntl(h, F_SETLK, &lock)==(-1) ){
rc = SQLITE_IOERR_RDLOCK;
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
goto end_unlock;
}
}
lock.l_type = F_UNLCK;
@ -1433,7 +1534,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){
if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = SHARED_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
goto end_unlock;
}
}
if( locktype==NO_LOCK ){
@ -1454,8 +1560,13 @@ static int unixUnlock(sqlite3_file *id, int locktype){
if( fcntl(h, F_SETLK, &lock)!=(-1) ){
pLock->locktype = NO_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
int tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
pLock->cnt = 1;
goto end_unlock;
}
}
@ -1478,6 +1589,8 @@ static int unixUnlock(sqlite3_file *id, int locktype){
}
}
}
end_unlock:
leaveMutex();
if( rc==SQLITE_OK ) pFile->locktype = locktype;
return rc;
@ -1565,14 +1678,11 @@ struct ByteRangeLockPB2
#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2)
/*
** Return 0 on success, 1 on failure. To match the behavior of the
** normal posix file locking (used in unixLock for example), we should
** provide 'richer' return codes - specifically to differentiate between
** 'file busy' and 'file system error' results.
*/
** Return SQLITE_OK on success, SQLITE_BUSY on failure.
*/
static int _AFPFSSetLock(
const char *path,
int fd,
unixFile *pFile,
unsigned long long offset,
unsigned long long length,
int setLockFlag
@ -1584,55 +1694,63 @@ static int _AFPFSSetLock(
pb.startEndFlag = 0;
pb.offset = offset;
pb.length = length;
pb.fd = fd;
pb.fd = pFile->h;
OSTRACE5("AFPLOCK setting lock %s for %d in range %llx:%llx\n",
(setLockFlag?"ON":"OFF"), fd, offset, length);
(setLockFlag?"ON":"OFF"), pFile->h, offset, length);
err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0);
if ( err==-1 ) {
OSTRACE4("AFPLOCK failed to fsctl() '%s' %d %s\n", path, errno,
strerror(errno));
return 1; /* error */
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 */
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
return rc;
} else {
return 0;
return SQLITE_OK;
}
}
/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, return
** non-zero. If the file is unlocked or holds only SHARED locks, then
** return zero.
*/
/* AFP-style reserved lock checking following the behavior of
** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
int r = 0;
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
assert( pFile );
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
/* Check if a thread in this process holds such a lock */
if( pFile->locktype>SHARED_LOCK ){
r = 1;
reserved = 1;
}
/* Otherwise see if some other process holds it.
*/
if ( !r ) {
/* lock the byte */
int failed = _AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1,1);
if (failed) {
/* if we failed to get the lock then someone else must have it */
r = 1;
} else {
if( !reserved ){
/* lock the RESERVED byte */
int lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1);
if( SQLITE_OK==lrc ){
/* if we succeeded in taking the reserved lock, unlock it to restore
** the original state */
_AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1, 0);
lrc = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1, 0);
} else {
/* if we failed to get the lock then someone else must have it */
reserved = 1;
}
if( IS_LOCK_ERROR(lrc) ){
rc=lrc;
}
}
OSTRACE3("TEST WR-LOCK %d %d\n", pFile->h, r);
*pResOut = r;
return SQLITE_OK;
OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
*pResOut = reserved;
return rc;
}
/* AFP-style locking following the behavior of unixLock, see the unixLock
@ -1682,9 +1800,9 @@ static int afpLock(sqlite3_file *id, int locktype){
|| (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK)
){
int failed;
failed = _AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 1);
failed = _AFPFSSetLock(context->filePath, pFile, PENDING_BYTE, 1, 1);
if (failed) {
rc = SQLITE_BUSY;
rc = failed;
goto afp_end_lock;
}
}
@ -1693,23 +1811,29 @@ static int afpLock(sqlite3_file *id, int locktype){
** operating system calls for the specified lock.
*/
if( locktype==SHARED_LOCK ){
int lk, failed;
int lk, lrc1, lrc2, lrc1Errno;
/* Now get the read-lock */
/* 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);
failed = _AFPFSSetLock(context->filePath, pFile->h,
SHARED_FIRST+context->sharedLockByte, 1, 1);
/* Drop the temporary PENDING lock */
if (_AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 0)) {
rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
goto afp_end_lock;
lrc1 = _AFPFSSetLock(context->filePath, pFile,
SHARED_FIRST+context->sharedLockByte, 1, 1);
if( IS_LOCK_ERROR(lrc1) ){
lrc1Errno = pFile->lastErrno;
}
/* Drop the temporary PENDING lock */
lrc2 = _AFPFSSetLock(context->filePath, pFile, PENDING_BYTE, 1, 0);
if( failed ){
rc = SQLITE_BUSY;
if( IS_LOCK_ERROR(lrc1) ) {
pFile->lastErrno = lrc1Errno;
rc = lrc1;
goto afp_end_lock;
} else if( IS_LOCK_ERROR(lrc2) ){
rc = lrc2;
goto afp_end_lock;
} else if( lrc1 != SQLITE_OK ) {
rc = lrc1;
} else {
pFile->locktype = SHARED_LOCK;
}
@ -1722,7 +1846,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->h, RESERVED_BYTE, 1,1);
failed = _AFPFSSetLock(context->filePath, pFile, RESERVED_BYTE, 1,1);
}
if (!failed && locktype == EXCLUSIVE_LOCK) {
/* Acquire an EXCLUSIVE lock */
@ -1730,22 +1854,21 @@ 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 (!_AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST +
context->sharedLockByte, 1, 0)) {
if (!(failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST +
context->sharedLockByte, 1, 0))) {
/* now attemmpt to get the exclusive lock range */
failed = _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST,
failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST,
SHARED_SIZE, 1);
if (failed && _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST +
context->sharedLockByte, 1, 1)) {
rc = SQLITE_IOERR_RDLOCK; /* this should never happen */
if (failed && (failed = _AFPFSSetLock(context->filePath, pFile,
SHARED_FIRST + context->sharedLockByte, 1, 1))) {
rc = failed;
}
} else {
/* */
rc = SQLITE_IOERR_UNLOCK; /* this should never happen */
rc = failed;
}
}
if( failed && rc == SQLITE_OK){
rc = SQLITE_BUSY;
if( failed ){
rc = failed;
}
}
@ -1777,7 +1900,7 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
assert( pFile );
OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
pFile->locktype, getpid());
assert( locktype<=SHARED_LOCK );
if( pFile->locktype<=locktype ){
return SQLITE_OK;
@ -1786,45 +1909,46 @@ 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 ){
int failed = 0;
/* unlock the exclusive range - then re-establish the shared lock */
if (pFile->locktype==EXCLUSIVE_LOCK) {
failed = _AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST,
failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST,
SHARED_SIZE, 0);
if (!failed) {
/* successfully removed the exclusive lock */
if (_AFPFSSetLock(context->filePath, pFile->h, SHARED_FIRST+
context->sharedLockByte, 1, 1)) {
if ((failed = _AFPFSSetLock(context->filePath, pFile, SHARED_FIRST+
context->sharedLockByte, 1, 1))) {
/* failed to re-establish our shared lock */
rc = SQLITE_IOERR_RDLOCK; /* This should never happen */
rc = failed;
}
} else {
/* This should never happen - failed to unlock the exclusive range */
rc = SQLITE_IOERR_UNLOCK;
rc = failed;
}
}
}
if (rc == SQLITE_OK && pFile->locktype>=PENDING_LOCK) {
if (_AFPFSSetLock(context->filePath, pFile->h, PENDING_BYTE, 1, 0)){
if ((failed = _AFPFSSetLock(context->filePath, pFile,
PENDING_BYTE, 1, 0))){
/* failed to release the pending lock */
rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
rc = failed;
}
}
if (rc == SQLITE_OK && pFile->locktype>=RESERVED_LOCK) {
if (_AFPFSSetLock(context->filePath, pFile->h, RESERVED_BYTE, 1, 0)) {
if ((failed = _AFPFSSetLock(context->filePath, pFile,
RESERVED_BYTE, 1, 0))) {
/* failed to release the reserved lock */
rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
rc = failed;
}
}
}
if( locktype==NO_LOCK ){
int failed = _AFPFSSetLock(context->filePath, pFile->h,
int failed = _AFPFSSetLock(context->filePath, pFile,
SHARED_FIRST + context->sharedLockByte, 1, 0);
if (failed) {
rc = SQLITE_IOERR_UNLOCK; /* This should never happen */
rc = failed;
}
}
if (rc == SQLITE_OK)
@ -1853,27 +1977,62 @@ static int afpClose(sqlite3_file *id) {
*/
typedef void flockLockingContext;
/* flock-style reserved lock checking following the behavior of
** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
int r = 1;
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
if (pFile->locktype != RESERVED_LOCK) {
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
/* Check if a thread in this process holds such a lock */
if( pFile->locktype>SHARED_LOCK ){
reserved = 1;
}
/* Otherwise see if some other process holds it. */
if( !reserved ){
/* attempt to get the lock */
int rc = flock(pFile->h, LOCK_EX | LOCK_NB);
if (!rc) {
int lrc = flock(pFile->h, LOCK_EX | LOCK_NB);
if( !lrc ){
/* got the lock, unlock it */
flock(pFile->h, LOCK_UN);
r = 0; /* no one has it reserved */
lrc = flock(pFile->h, LOCK_UN);
if ( lrc ) {
int tErrno = errno;
/* unlock failed with an error */
lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(lrc) ){
pFile->lastErrno = tErrno;
rc = lrc;
}
}
} else {
int tErrno = errno;
reserved = 1;
/* someone else might have it reserved */
lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(lrc) ){
pFile->lastErrno = tErrno;
rc = lrc;
}
}
}
OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
*pResOut = r;
return SQLITE_OK;
*pResOut = reserved;
return rc;
}
static int flockLock(sqlite3_file *id, int locktype) {
int rc = SQLITE_OK;
int lrc;
unixFile *pFile = (unixFile*)id;
assert( pFile );
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (pFile->locktype > NO_LOCK) {
@ -1882,20 +2041,29 @@ static int flockLock(sqlite3_file *id, int locktype) {
}
/* grab an exclusive lock */
int rc = flock(pFile->h, LOCK_EX | LOCK_NB);
if (rc) {
if (flock(pFile->h, LOCK_EX | LOCK_NB)) {
int tErrno = errno;
/* didn't get, must be busy */
return SQLITE_BUSY;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
} else {
/* got it, set the type and return ok */
pFile->locktype = locktype;
return SQLITE_OK;
}
OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype),
rc==SQLITE_OK ? "ok" : "failed");
return rc;
}
static int flockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
assert( pFile );
OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
pFile->locktype, getpid());
assert( locktype<=SHARED_LOCK );
/* no-op if possible */
@ -1911,9 +2079,14 @@ static int flockUnlock(sqlite3_file *id, int locktype) {
/* no, really, unlock. */
int rc = flock(pFile->h, LOCK_UN);
if (rc)
return SQLITE_IOERR_UNLOCK;
else {
if (rc) {
int r, tErrno = errno;
r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(r) ){
pFile->lastErrno = tErrno;
}
return r;
} else {
pFile->locktype = NO_LOCK;
return SQLITE_OK;
}
@ -1931,27 +2104,50 @@ static int flockClose(sqlite3_file *id) {
#pragma mark Old-School .lock file based locking
/* Dotlock-style reserved lock checking following the behavior of
** unixCheckReservedLock, see the unixCheckReservedLock function comments */
static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
int r = 1;
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
char *zLockFile = (char *)pFile->lockingContext;
if (pFile->locktype != RESERVED_LOCK) {
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
/* Check if a thread in this process holds such a lock */
if( pFile->locktype>SHARED_LOCK ){
reserved = 1;
}
/* Otherwise see if some other process holds it. */
if( !reserved ){
char *zLockFile = (char *)pFile->lockingContext;
struct stat statBuf;
if (lstat(zLockFile, &statBuf) != 0){
if( lstat(zLockFile, &statBuf)==0 ){
/* file exists, someone else has the lock */
reserved = 1;
}else{
/* file does not exist, we could have it if we want it */
r = 0;
int tErrno = errno;
if( ENOENT != tErrno ){
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
pFile->lastErrno = tErrno;
}
}
}
OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
*pResOut = r;
return SQLITE_OK;
*pResOut = reserved;
return rc;
}
static int dotlockLock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
int fd;
char *zLockFile = (char *)pFile->lockingContext;
int rc=SQLITE_OK;
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
@ -1960,32 +2156,48 @@ static int dotlockLock(sqlite3_file *id, int locktype) {
/* Always update the timestamp on the old file */
utimes(zLockFile, NULL);
return SQLITE_OK;
rc = SQLITE_OK;
goto dotlock_end_lock;
}
/* check to see if lock file already exists */
struct stat statBuf;
if (lstat(zLockFile,&statBuf) == 0){
return SQLITE_BUSY; /* it does, busy */
rc = SQLITE_BUSY; /* it does, busy */
goto dotlock_end_lock;
}
/* grab an exclusive lock */
fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
if( fd<0 ){
/* failed to open/create the file, someone else may have stolen the lock */
return SQLITE_BUSY;
}
int tErrno = errno;
if( EEXIST == tErrno ){
rc = SQLITE_BUSY;
} else {
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
}
goto dotlock_end_lock;
}
close(fd);
/* got it, set the type and return ok */
pFile->locktype = locktype;
return SQLITE_OK;
dotlock_end_lock:
return rc;
}
static int dotlockUnlock(sqlite3_file *id, int locktype) {
unixFile *pFile = (unixFile*)id;
char *zLockFile = (char *)pFile->lockingContext;
assert( pFile );
OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype,
pFile->locktype, getpid());
assert( locktype<=SHARED_LOCK );
/* no-op if possible */
@ -2000,7 +2212,16 @@ static int dotlockUnlock(sqlite3_file *id, int locktype) {
}
/* no, really, unlock. */
unlink(zLockFile);
if (unlink(zLockFile) ) {
int rc, tErrno = errno;
if( ENOENT != tErrno ){
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
}
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
return rc;
}
pFile->locktype = NO_LOCK;
return SQLITE_OK;
}
@ -2202,7 +2423,8 @@ static int fillInUnixFile(
break;
#endif
}
pNew->lastErrno = 0;
if( rc!=SQLITE_OK ){
if( dirfd>=0 ) close(dirfd);
close(h);

View File

@ -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.392 2008/08/12 14:51:30 drh Exp $
** @(#) $Id: sqlite.h.in,v 1.393 2008/08/22 00:22:35 aswift Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@ -516,6 +516,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8))
#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8))
#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))
#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8))
/*
** CAPI3REF: Flags For File Open Operations {H10230} <H11120> <H12700>