1033 lines
30 KiB
C
Executable File
1033 lines
30 KiB
C
Executable File
/*
|
|
** 2006 Feb 14
|
|
**
|
|
** 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 contains code that is specific to OS/2.
|
|
*/
|
|
|
|
#include "sqliteInt.h"
|
|
|
|
#if OS_OS2
|
|
|
|
/*
|
|
** Macros used to determine whether or not to use threads.
|
|
*/
|
|
#if defined(THREADSAFE) && THREADSAFE
|
|
# define SQLITE_OS2_THREADS 1
|
|
#endif
|
|
|
|
/*
|
|
** Include code that is common to all os_*.c files
|
|
*/
|
|
#include "os_common.h"
|
|
|
|
/*
|
|
** The os2File structure is subclass of OsFile specific for the OS/2
|
|
** protability layer.
|
|
*/
|
|
typedef struct os2File os2File;
|
|
struct os2File {
|
|
IoMethod const *pMethod; /* Always the first entry */
|
|
HFILE h; /* Handle for accessing the file */
|
|
int delOnClose; /* True if file is to be deleted on close */
|
|
char* pathToDel; /* Name of file to delete on close */
|
|
unsigned char locktype; /* Type of lock currently held on this file */
|
|
};
|
|
|
|
/*
|
|
** Do not include any of the File I/O interface procedures if the
|
|
** SQLITE_OMIT_DISKIO macro is defined (indicating that there database
|
|
** will be in-memory only)
|
|
*/
|
|
#ifndef SQLITE_OMIT_DISKIO
|
|
|
|
/*
|
|
** Delete the named file
|
|
*/
|
|
int sqlite3Os2Delete( const char *zFilename ){
|
|
APIRET rc = NO_ERROR;
|
|
|
|
rc = DosDelete( (PSZ)zFilename );
|
|
OSTRACE2( "DELETE \"%s\"\n", zFilename );
|
|
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Return TRUE if the named file exists.
|
|
*/
|
|
int sqlite3Os2FileExists( const char *zFilename ){
|
|
FILESTATUS3 fsts3ConfigInfo;
|
|
memset(&fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo));
|
|
return DosQueryPathInfo( (PSZ)zFilename, FIL_STANDARD,
|
|
&fsts3ConfigInfo, sizeof(FILESTATUS3) ) == NO_ERROR;
|
|
}
|
|
|
|
/* Forward declaration */
|
|
int allocateOs2File( os2File *pInit, OsFile **pld );
|
|
|
|
/*
|
|
** Attempt to open a file for both reading and writing. If that
|
|
** fails, try opening it read-only. If the file does not exist,
|
|
** try to create it.
|
|
**
|
|
** On success, a handle for the open file is written to *id
|
|
** and *pReadonly is set to 0 if the file was opened for reading and
|
|
** writing or 1 if the file was opened read-only. The function returns
|
|
** SQLITE_OK.
|
|
**
|
|
** On failure, the function returns SQLITE_CANTOPEN and leaves
|
|
** *id and *pReadonly unchanged.
|
|
*/
|
|
int sqlite3Os2OpenReadWrite(
|
|
const char *zFilename,
|
|
OsFile **pld,
|
|
int *pReadonly
|
|
){
|
|
os2File f;
|
|
HFILE hf;
|
|
ULONG ulAction;
|
|
APIRET rc = NO_ERROR;
|
|
|
|
assert( *pld == 0 );
|
|
rc = DosOpen( (PSZ)zFilename, &hf, &ulAction, 0L,
|
|
FILE_ARCHIVED | FILE_NORMAL,
|
|
OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
|
|
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM |
|
|
OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, (PEAOP2)NULL );
|
|
if( rc != NO_ERROR ){
|
|
rc = DosOpen( (PSZ)zFilename, &hf, &ulAction, 0L,
|
|
FILE_ARCHIVED | FILE_NORMAL,
|
|
OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
|
|
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM |
|
|
OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY, (PEAOP2)NULL );
|
|
if( rc != NO_ERROR ){
|
|
return SQLITE_CANTOPEN;
|
|
}
|
|
*pReadonly = 1;
|
|
}
|
|
else{
|
|
*pReadonly = 0;
|
|
}
|
|
f.h = hf;
|
|
f.locktype = NO_LOCK;
|
|
f.delOnClose = 0;
|
|
f.pathToDel = NULL;
|
|
OpenCounter(+1);
|
|
OSTRACE3( "OPEN R/W %d \"%s\"\n", hf, zFilename );
|
|
return allocateOs2File( &f, pld );
|
|
}
|
|
|
|
|
|
/*
|
|
** Attempt to open a new file for exclusive access by this process.
|
|
** The file will be opened for both reading and writing. To avoid
|
|
** a potential security problem, we do not allow the file to have
|
|
** previously existed. Nor do we allow the file to be a symbolic
|
|
** link.
|
|
**
|
|
** If delFlag is true, then make arrangements to automatically delete
|
|
** the file when it is closed.
|
|
**
|
|
** On success, write the file handle into *id and return SQLITE_OK.
|
|
**
|
|
** On failure, return SQLITE_CANTOPEN.
|
|
*/
|
|
int sqlite3Os2OpenExclusive( const char *zFilename, OsFile **pld, int delFlag ){
|
|
os2File f;
|
|
HFILE hf;
|
|
ULONG ulAction;
|
|
APIRET rc = NO_ERROR;
|
|
|
|
assert( *pld == 0 );
|
|
rc = DosOpen( (PSZ)zFilename, &hf, &ulAction, 0L, FILE_NORMAL,
|
|
OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS,
|
|
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM |
|
|
OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE, (PEAOP2)NULL );
|
|
if( rc != NO_ERROR ){
|
|
return SQLITE_CANTOPEN;
|
|
}
|
|
|
|
f.h = hf;
|
|
f.locktype = NO_LOCK;
|
|
f.delOnClose = delFlag ? 1 : 0;
|
|
f.pathToDel = delFlag ? sqlite3OsFullPathname( zFilename ) : NULL;
|
|
OpenCounter( +1 );
|
|
if( delFlag ) DosForceDelete( (PSZ)sqlite3OsFullPathname( zFilename ) );
|
|
OSTRACE3( "OPEN EX %d \"%s\"\n", hf, sqlite3OsFullPathname ( zFilename ) );
|
|
return allocateOs2File( &f, pld );
|
|
}
|
|
|
|
/*
|
|
** Attempt to open a new file for read-only access.
|
|
**
|
|
** On success, write the file handle into *id and return SQLITE_OK.
|
|
**
|
|
** On failure, return SQLITE_CANTOPEN.
|
|
*/
|
|
int sqlite3Os2OpenReadOnly( const char *zFilename, OsFile **pld ){
|
|
os2File f;
|
|
HFILE hf;
|
|
ULONG ulAction;
|
|
APIRET rc = NO_ERROR;
|
|
|
|
assert( *pld == 0 );
|
|
rc = DosOpen( (PSZ)zFilename, &hf, &ulAction, 0L,
|
|
FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS,
|
|
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_RANDOM |
|
|
OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY, (PEAOP2)NULL );
|
|
if( rc != NO_ERROR ){
|
|
return SQLITE_CANTOPEN;
|
|
}
|
|
f.h = hf;
|
|
f.locktype = NO_LOCK;
|
|
f.delOnClose = 0;
|
|
f.pathToDel = NULL;
|
|
OpenCounter( +1 );
|
|
OSTRACE3( "OPEN RO %d \"%s\"\n", hf, zFilename );
|
|
return allocateOs2File( &f, pld );
|
|
}
|
|
|
|
/*
|
|
** Attempt to open a file descriptor for the directory that contains a
|
|
** file. This file descriptor can be used to fsync() the directory
|
|
** in order to make sure the creation of a new file is actually written
|
|
** to disk.
|
|
**
|
|
** This routine is only meaningful for Unix. It is a no-op under
|
|
** OS/2 since OS/2 does not support hard links.
|
|
**
|
|
** On success, a handle for a previously open file is at *id is
|
|
** updated with the new directory file descriptor and SQLITE_OK is
|
|
** returned.
|
|
**
|
|
** On failure, the function returns SQLITE_CANTOPEN and leaves
|
|
** *id unchanged.
|
|
*/
|
|
int os2OpenDirectory(
|
|
OsFile *id,
|
|
const char *zDirname
|
|
){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Create a temporary file name in zBuf. zBuf must be big enough to
|
|
** hold at least SQLITE_TEMPNAME_SIZE characters.
|
|
*/
|
|
int sqlite3Os2TempFileName( char *zBuf ){
|
|
static const unsigned char zChars[] =
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"0123456789";
|
|
int i, j;
|
|
PSZ zTempPath = 0;
|
|
if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){
|
|
if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){
|
|
if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){
|
|
ULONG ulDriveNum = 0, ulDriveMap = 0;
|
|
DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap );
|
|
sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) );
|
|
}
|
|
}
|
|
}
|
|
/* strip off a trailing slashes or backslashes, otherwise we would get *
|
|
* multiple (back)slashes which causes DosOpen() to fail */
|
|
j = strlen(zTempPath);
|
|
while( j > 0 && zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ){
|
|
j--;
|
|
}
|
|
zTempPath[j] = '\0';
|
|
for(;;){
|
|
sprintf( zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath );
|
|
j = strlen( zBuf );
|
|
sqlite3Randomness( 15, &zBuf[j] );
|
|
for( i = 0; i < 15; i++, j++ ){
|
|
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
|
}
|
|
zBuf[j] = 0;
|
|
if( !sqlite3OsFileExists( zBuf ) ) break;
|
|
}
|
|
OSTRACE2( "TEMP FILENAME: %s\n", zBuf );
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Close a file.
|
|
*/
|
|
int os2Close( OsFile **pld ){
|
|
os2File *pFile;
|
|
APIRET rc = NO_ERROR;
|
|
if( pld && (pFile = (os2File*)*pld) != 0 ){
|
|
OSTRACE2( "CLOSE %d\n", pFile->h );
|
|
rc = DosClose( pFile->h );
|
|
pFile->locktype = NO_LOCK;
|
|
if( pFile->delOnClose != 0 ){
|
|
rc = DosForceDelete( (PSZ)pFile->pathToDel );
|
|
}
|
|
*pld = 0;
|
|
OpenCounter( -1 );
|
|
}
|
|
|
|
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Read data from a file into a buffer. Return SQLITE_OK if all
|
|
** bytes were read successfully and SQLITE_IOERR if anything goes
|
|
** wrong.
|
|
*/
|
|
int os2Read( OsFile *id, void *pBuf, int amt ){
|
|
ULONG got;
|
|
assert( id!=0 );
|
|
SimulateIOError( return SQLITE_IOERR );
|
|
OSTRACE3( "READ %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
|
|
DosRead( ((os2File*)id)->h, pBuf, amt, &got );
|
|
if (got == (ULONG)amt)
|
|
return SQLITE_OK;
|
|
else if (got == 0)
|
|
return SQLITE_IOERR_READ;
|
|
else {
|
|
memset(&((char*)pBuf)[got], 0, amt-got);
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Write data from a buffer into a file. Return SQLITE_OK on success
|
|
** or some other error code on failure.
|
|
*/
|
|
int os2Write( OsFile *id, const void *pBuf, int amt ){
|
|
APIRET rc = NO_ERROR;
|
|
ULONG wrote;
|
|
assert( id!=0 );
|
|
SimulateIOError( return SQLITE_IOERR );
|
|
SimulateDiskfullError( return SQLITE_FULL );
|
|
OSTRACE3( "WRITE %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
|
|
while( amt > 0 &&
|
|
(rc = DosWrite( ((os2File*)id)->h, (PVOID)pBuf, amt, &wrote )) && wrote > 0 ){
|
|
amt -= wrote;
|
|
pBuf = &((char*)pBuf)[wrote];
|
|
}
|
|
|
|
return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Move the read/write pointer in a file.
|
|
*/
|
|
int os2Seek( OsFile *id, i64 offset ){
|
|
APIRET rc = NO_ERROR;
|
|
ULONG filePointer = 0L;
|
|
assert( id!=0 );
|
|
rc = DosSetFilePtr( ((os2File*)id)->h, offset, FILE_BEGIN, &filePointer );
|
|
OSTRACE3( "SEEK %d %lld\n", ((os2File*)id)->h, offset );
|
|
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Make sure all writes to a particular file are committed to disk.
|
|
*/
|
|
int os2Sync( OsFile *id, int dataOnly ){
|
|
assert( id!=0 );
|
|
OSTRACE3( "SYNC %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
|
|
return DosResetBuffer( ((os2File*)id)->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Sync the directory zDirname. This is a no-op on operating systems other
|
|
** than UNIX.
|
|
*/
|
|
int sqlite3Os2SyncDirectory( const char *zDirname ){
|
|
SimulateIOError( return SQLITE_IOERR );
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Truncate an open file to a specified size
|
|
*/
|
|
int os2Truncate( OsFile *id, i64 nByte ){
|
|
APIRET rc = NO_ERROR;
|
|
ULONG upperBits = nByte>>32;
|
|
assert( id!=0 );
|
|
OSTRACE3( "TRUNCATE %d %lld\n", ((os2File*)id)->h, nByte );
|
|
SimulateIOError( return SQLITE_IOERR );
|
|
rc = DosSetFilePtr( ((os2File*)id)->h, nByte, FILE_BEGIN, &upperBits );
|
|
if( rc != NO_ERROR ){
|
|
return SQLITE_IOERR;
|
|
}
|
|
rc = DosSetFilePtr( ((os2File*)id)->h, 0L, FILE_END, &upperBits );
|
|
return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Determine the current size of a file in bytes
|
|
*/
|
|
int os2FileSize( OsFile *id, i64 *pSize ){
|
|
APIRET rc = NO_ERROR;
|
|
FILESTATUS3 fsts3FileInfo;
|
|
memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
|
|
assert( id!=0 );
|
|
SimulateIOError( return SQLITE_IOERR );
|
|
rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
|
|
if( rc == NO_ERROR ){
|
|
*pSize = fsts3FileInfo.cbFile;
|
|
return SQLITE_OK;
|
|
}
|
|
else{
|
|
return SQLITE_IOERR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Acquire a reader lock.
|
|
*/
|
|
static int getReadLock( os2File *id ){
|
|
FILELOCK LockArea,
|
|
UnlockArea;
|
|
memset(&LockArea, 0, sizeof(LockArea));
|
|
memset(&UnlockArea, 0, sizeof(UnlockArea));
|
|
LockArea.lOffset = SHARED_FIRST;
|
|
LockArea.lRange = SHARED_SIZE;
|
|
UnlockArea.lOffset = 0L;
|
|
UnlockArea.lRange = 0L;
|
|
return DosSetFileLocks( id->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
|
|
/*
|
|
** Undo a readlock
|
|
*/
|
|
static int unlockReadLock( os2File *id ){
|
|
FILELOCK LockArea,
|
|
UnlockArea;
|
|
memset(&LockArea, 0, sizeof(LockArea));
|
|
memset(&UnlockArea, 0, sizeof(UnlockArea));
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = SHARED_FIRST;
|
|
UnlockArea.lRange = SHARED_SIZE;
|
|
return DosSetFileLocks( id->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
|
|
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
|
/*
|
|
** Check that a given pathname is a directory and is writable
|
|
**
|
|
*/
|
|
int sqlite3Os2IsDirWritable( char *zDirname ){
|
|
FILESTATUS3 fsts3ConfigInfo;
|
|
APIRET rc = NO_ERROR;
|
|
memset(&fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo));
|
|
if( zDirname==0 ) return 0;
|
|
if( strlen(zDirname)>CCHMAXPATH ) return 0;
|
|
rc = DosQueryPathInfo( (PSZ)zDirname, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) );
|
|
if( rc != NO_ERROR ) return 0;
|
|
if( (fsts3ConfigInfo.attrFile & FILE_DIRECTORY) != FILE_DIRECTORY ) return 0;
|
|
|
|
return 1;
|
|
}
|
|
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
|
|
|
|
/*
|
|
** Lock the file with the lock specified by parameter locktype - one
|
|
** of the following:
|
|
**
|
|
** (1) SHARED_LOCK
|
|
** (2) RESERVED_LOCK
|
|
** (3) PENDING_LOCK
|
|
** (4) EXCLUSIVE_LOCK
|
|
**
|
|
** Sometimes when requesting one lock state, additional lock states
|
|
** are inserted in between. The locking might fail on one of the later
|
|
** transitions leaving the lock state different from what it started but
|
|
** still short of its goal. The following chart shows the allowed
|
|
** transitions and the inserted intermediate states:
|
|
**
|
|
** UNLOCKED -> SHARED
|
|
** SHARED -> RESERVED
|
|
** SHARED -> (PENDING) -> EXCLUSIVE
|
|
** RESERVED -> (PENDING) -> EXCLUSIVE
|
|
** PENDING -> EXCLUSIVE
|
|
**
|
|
** This routine will only increase a lock. The os2Unlock() routine
|
|
** erases all locks at once and returns us immediately to locking level 0.
|
|
** It is not possible to lower the locking level one step at a time. You
|
|
** must go straight to locking level 0.
|
|
*/
|
|
int os2Lock( OsFile *id, int locktype ){
|
|
APIRET rc = SQLITE_OK; /* Return code from subroutines */
|
|
APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
|
|
int newLocktype; /* Set id->locktype to this value before exiting */
|
|
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
|
|
FILELOCK LockArea,
|
|
UnlockArea;
|
|
os2File *pFile = (os2File*)id;
|
|
memset(&LockArea, 0, sizeof(LockArea));
|
|
memset(&UnlockArea, 0, sizeof(UnlockArea));
|
|
assert( pFile!=0 );
|
|
OSTRACE4( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype );
|
|
|
|
/* If there is already a lock of this type or more restrictive on the
|
|
** OsFile, do nothing. Don't use the end_lock: exit path, as
|
|
** sqlite3OsEnterMutex() hasn't been called yet.
|
|
*/
|
|
if( pFile->locktype>=locktype ){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* Make sure the locking sequence is correct
|
|
*/
|
|
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
|
|
assert( locktype!=PENDING_LOCK );
|
|
assert( locktype!=RESERVED_LOCK || pFile->locktype==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 = pFile->locktype;
|
|
if( pFile->locktype==NO_LOCK
|
|
|| (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
|
|
){
|
|
int cnt = 3;
|
|
|
|
LockArea.lOffset = PENDING_BYTE;
|
|
LockArea.lRange = 1L;
|
|
UnlockArea.lOffset = 0L;
|
|
UnlockArea.lRange = 0L;
|
|
|
|
while( cnt-->0 && (res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L) )!=NO_ERROR ){
|
|
/* Try 3 times to get the pending lock. The pending lock might be
|
|
** held by another reader process who will release it momentarily.
|
|
*/
|
|
OSTRACE2( "could not get a PENDING lock. cnt=%d\n", cnt );
|
|
DosSleep(1);
|
|
}
|
|
gotPendingLock = res;
|
|
}
|
|
|
|
/* Acquire a shared lock
|
|
*/
|
|
if( locktype==SHARED_LOCK && res ){
|
|
assert( pFile->locktype==NO_LOCK );
|
|
res = getReadLock(pFile);
|
|
if( res == NO_ERROR ){
|
|
newLocktype = SHARED_LOCK;
|
|
}
|
|
}
|
|
|
|
/* Acquire a RESERVED lock
|
|
*/
|
|
if( locktype==RESERVED_LOCK && res ){
|
|
assert( pFile->locktype==SHARED_LOCK );
|
|
LockArea.lOffset = RESERVED_BYTE;
|
|
LockArea.lRange = 1L;
|
|
UnlockArea.lOffset = 0L;
|
|
UnlockArea.lRange = 0L;
|
|
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
if( res == NO_ERROR ){
|
|
newLocktype = RESERVED_LOCK;
|
|
}
|
|
}
|
|
|
|
/* Acquire a PENDING lock
|
|
*/
|
|
if( locktype==EXCLUSIVE_LOCK && res ){
|
|
newLocktype = PENDING_LOCK;
|
|
gotPendingLock = 0;
|
|
}
|
|
|
|
/* Acquire an EXCLUSIVE lock
|
|
*/
|
|
if( locktype==EXCLUSIVE_LOCK && res ){
|
|
assert( pFile->locktype>=SHARED_LOCK );
|
|
res = unlockReadLock(pFile);
|
|
OSTRACE2( "unreadlock = %d\n", res );
|
|
LockArea.lOffset = SHARED_FIRST;
|
|
LockArea.lRange = SHARED_SIZE;
|
|
UnlockArea.lOffset = 0L;
|
|
UnlockArea.lRange = 0L;
|
|
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
if( res == NO_ERROR ){
|
|
newLocktype = EXCLUSIVE_LOCK;
|
|
}else{
|
|
OSTRACE2( "error-code = %d\n", res );
|
|
}
|
|
}
|
|
|
|
/* If we are holding a PENDING lock that ought to be released, then
|
|
** release it now.
|
|
*/
|
|
if( gotPendingLock && locktype==SHARED_LOCK ){
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = PENDING_BYTE;
|
|
UnlockArea.lRange = 1L;
|
|
DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
|
|
/* Update the state of the lock has held in the file descriptor then
|
|
** return the appropriate result code.
|
|
*/
|
|
if( res == NO_ERROR ){
|
|
rc = SQLITE_OK;
|
|
}else{
|
|
OSTRACE4( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
|
|
locktype, newLocktype );
|
|
rc = SQLITE_BUSY;
|
|
}
|
|
pFile->locktype = newLocktype;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** 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, otherwise zero.
|
|
*/
|
|
int os2CheckReservedLock( OsFile *id ){
|
|
APIRET rc = NO_ERROR;
|
|
os2File *pFile = (os2File*)id;
|
|
assert( pFile!=0 );
|
|
if( pFile->locktype>=RESERVED_LOCK ){
|
|
rc = 1;
|
|
OSTRACE3( "TEST WR-LOCK %d %d (local)\n", pFile->h, rc );
|
|
}else{
|
|
FILELOCK LockArea,
|
|
UnlockArea;
|
|
memset(&LockArea, 0, sizeof(LockArea));
|
|
memset(&UnlockArea, 0, sizeof(UnlockArea));
|
|
LockArea.lOffset = RESERVED_BYTE;
|
|
LockArea.lRange = 1L;
|
|
UnlockArea.lOffset = 0L;
|
|
UnlockArea.lRange = 0L;
|
|
rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
if( rc == NO_ERROR ){
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = RESERVED_BYTE;
|
|
UnlockArea.lRange = 1L;
|
|
rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
OSTRACE3( "TEST WR-LOCK %d %d (remote)\n", pFile->h, rc );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Lower the locking level on file descriptor id to locktype. locktype
|
|
** must be either NO_LOCK or SHARED_LOCK.
|
|
**
|
|
** If the locking level of the file descriptor is already at or below
|
|
** the requested locking level, this routine is a no-op.
|
|
**
|
|
** It is not possible for this routine to fail if the second argument
|
|
** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
|
|
** might return SQLITE_IOERR;
|
|
*/
|
|
int os2Unlock( OsFile *id, int locktype ){
|
|
int type;
|
|
APIRET rc = SQLITE_OK;
|
|
os2File *pFile = (os2File*)id;
|
|
FILELOCK LockArea,
|
|
UnlockArea;
|
|
memset(&LockArea, 0, sizeof(LockArea));
|
|
memset(&UnlockArea, 0, sizeof(UnlockArea));
|
|
assert( pFile!=0 );
|
|
assert( locktype<=SHARED_LOCK );
|
|
OSTRACE4( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype );
|
|
type = pFile->locktype;
|
|
if( type>=EXCLUSIVE_LOCK ){
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = SHARED_FIRST;
|
|
UnlockArea.lRange = SHARED_SIZE;
|
|
DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
|
|
/* This should never happen. We should always be able to
|
|
** reacquire the read lock */
|
|
rc = SQLITE_IOERR;
|
|
}
|
|
}
|
|
if( type>=RESERVED_LOCK ){
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = RESERVED_BYTE;
|
|
UnlockArea.lRange = 1L;
|
|
DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
|
|
unlockReadLock(pFile);
|
|
}
|
|
if( type>=PENDING_LOCK ){
|
|
LockArea.lOffset = 0L;
|
|
LockArea.lRange = 0L;
|
|
UnlockArea.lOffset = PENDING_BYTE;
|
|
UnlockArea.lRange = 1L;
|
|
DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 2000L, 1L );
|
|
}
|
|
pFile->locktype = locktype;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Turn a relative pathname into a full pathname. Return a pointer
|
|
** to the full pathname stored in space obtained from sqliteMalloc().
|
|
** The calling function is responsible for freeing this space once it
|
|
** is no longer needed.
|
|
*/
|
|
char *sqlite3Os2FullPathname( const char *zRelative ){
|
|
char *zFull = 0;
|
|
if( strchr(zRelative, ':') ){
|
|
sqlite3SetString( &zFull, zRelative, (char*)0 );
|
|
}else{
|
|
ULONG ulDriveNum = 0;
|
|
ULONG ulDriveMap = 0;
|
|
ULONG cbzBufLen = SQLITE_TEMPNAME_SIZE;
|
|
char zDrive[2];
|
|
char *zBuff;
|
|
|
|
zBuff = sqliteMalloc( cbzBufLen );
|
|
if( zBuff != 0 ){
|
|
DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap );
|
|
if( DosQueryCurrentDir( ulDriveNum, (PBYTE)zBuff, &cbzBufLen ) == NO_ERROR ){
|
|
sprintf( zDrive, "%c", (char)('A' + ulDriveNum - 1) );
|
|
sqlite3SetString( &zFull, zDrive, ":\\", zBuff,
|
|
"\\", zRelative, (char*)0 );
|
|
}
|
|
sqliteFree( zBuff );
|
|
}
|
|
}
|
|
return zFull;
|
|
}
|
|
|
|
/*
|
|
** The fullSync option is meaningless on os2, or correct me if I'm wrong. This is a no-op.
|
|
** From os_unix.c: Change the value of the fullsync flag in the given file descriptor.
|
|
** From os_unix.c: ((unixFile*)id)->fullSync = v;
|
|
*/
|
|
static void os2SetFullSync( OsFile *id, int v ){
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Return the underlying file handle for an OsFile
|
|
*/
|
|
static int os2FileHandle( OsFile *id ){
|
|
return (int)((os2File*)id)->h;
|
|
}
|
|
|
|
/*
|
|
** Return an integer that indices the type of lock currently held
|
|
** by this handle. (Used for testing and analysis only.)
|
|
*/
|
|
static int os2LockState( OsFile *id ){
|
|
return ((os2File*)id)->locktype;
|
|
}
|
|
|
|
/*
|
|
** Return the sector size in bytes of the underlying block device for
|
|
** the specified file. This is almost always 512 bytes, but may be
|
|
** larger for some devices.
|
|
**
|
|
** SQLite code assumes this function cannot fail. It also assumes that
|
|
** if two files are created in the same file-system directory (i.e.
|
|
** a database and it's journal file) that the sector size will be the
|
|
** same for both.
|
|
*/
|
|
static int os2SectorSize(OsFile *id){
|
|
return SQLITE_DEFAULT_SECTOR_SIZE;
|
|
}
|
|
|
|
/*
|
|
** This vector defines all the methods that can operate on an OsFile
|
|
** for os2.
|
|
*/
|
|
static const IoMethod sqlite3Os2IoMethod = {
|
|
os2Close,
|
|
os2OpenDirectory,
|
|
os2Read,
|
|
os2Write,
|
|
os2Seek,
|
|
os2Truncate,
|
|
os2Sync,
|
|
os2SetFullSync,
|
|
os2FileHandle,
|
|
os2FileSize,
|
|
os2Lock,
|
|
os2Unlock,
|
|
os2LockState,
|
|
os2CheckReservedLock,
|
|
os2SectorSize,
|
|
};
|
|
|
|
/*
|
|
** Allocate memory for an OsFile. Initialize the new OsFile
|
|
** to the value given in pInit and return a pointer to the new
|
|
** OsFile. If we run out of memory, close the file and return NULL.
|
|
*/
|
|
int allocateOs2File( os2File *pInit, OsFile **pld ){
|
|
os2File *pNew;
|
|
pNew = sqliteMalloc( sizeof(*pNew) );
|
|
if( pNew==0 ){
|
|
DosClose( pInit->h );
|
|
*pld = 0;
|
|
return SQLITE_NOMEM;
|
|
}else{
|
|
*pNew = *pInit;
|
|
pNew->pMethod = &sqlite3Os2IoMethod;
|
|
pNew->locktype = NO_LOCK;
|
|
*pld = (OsFile*)pNew;
|
|
OpenCounter(+1);
|
|
return SQLITE_OK;
|
|
}
|
|
}
|
|
|
|
#endif /* SQLITE_OMIT_DISKIO */
|
|
/***************************************************************************
|
|
** Everything above deals with file I/O. Everything that follows deals
|
|
** with other miscellanous aspects of the operating system interface
|
|
****************************************************************************/
|
|
|
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
/*
|
|
** Interfaces for opening a shared library, finding entry points
|
|
** within the shared library, and closing the shared library.
|
|
*/
|
|
void *sqlite3Os2Dlopen(const char *zFilename){
|
|
UCHAR loadErr[256];
|
|
HMODULE hmod;
|
|
APIRET rc;
|
|
rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilename, &hmod);
|
|
if (rc != NO_ERROR) return 0;
|
|
return (void*)hmod;
|
|
}
|
|
void *sqlite3Os2Dlsym(void *pHandle, const char *zSymbol){
|
|
PFN pfn;
|
|
APIRET rc;
|
|
rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn);
|
|
if (rc != NO_ERROR) {
|
|
/* if the symbol itself was not found, search again for the same
|
|
* symbol with an extra underscore, that might be needed depending
|
|
* on the calling convention */
|
|
char _zSymbol[256] = "_";
|
|
strncat(_zSymbol, zSymbol, 255);
|
|
rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn);
|
|
}
|
|
if (rc != NO_ERROR) return 0;
|
|
return (void *)pfn;
|
|
}
|
|
int sqlite3Os2Dlclose(void *pHandle){
|
|
return DosFreeModule((HMODULE)pHandle);
|
|
}
|
|
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
|
|
|
|
|
/*
|
|
** Get information to seed the random number generator. The seed
|
|
** is written into the buffer zBuf[256]. The calling function must
|
|
** supply a sufficiently large buffer.
|
|
*/
|
|
int sqlite3Os2RandomSeed( char *zBuf ){
|
|
/* We have to initialize zBuf to prevent valgrind from reporting
|
|
** errors. The reports issued by valgrind are incorrect - we would
|
|
** prefer that the randomness be increased by making use of the
|
|
** uninitialized space in zBuf - but valgrind errors tend to worry
|
|
** some users. Rather than argue, it seems easier just to initialize
|
|
** the whole array and silence valgrind, even if that means less randomness
|
|
** in the random seed.
|
|
**
|
|
** When testing, initializing zBuf[] to zero is all we do. That means
|
|
** that we always use the same random number sequence. This makes the
|
|
** tests repeatable.
|
|
*/
|
|
memset( zBuf, 0, 256 );
|
|
DosGetDateTime( (PDATETIME)zBuf );
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Sleep for a little while. Return the amount of time slept.
|
|
*/
|
|
int sqlite3Os2Sleep( int ms ){
|
|
DosSleep( ms );
|
|
return ms;
|
|
}
|
|
|
|
/*
|
|
** Static variables used for thread synchronization
|
|
*/
|
|
static int inMutex = 0;
|
|
#ifdef SQLITE_OS2_THREADS
|
|
static ULONG mutexOwner;
|
|
#endif
|
|
|
|
/*
|
|
** The following pair of routines implement mutual exclusion for
|
|
** multi-threaded processes. Only a single thread is allowed to
|
|
** executed code that is surrounded by EnterMutex() and LeaveMutex().
|
|
**
|
|
** SQLite uses only a single Mutex. There is not much critical
|
|
** code and what little there is executes quickly and without blocking.
|
|
*/
|
|
void sqlite3Os2EnterMutex(){
|
|
#ifdef SQLITE_OS2_THREADS
|
|
PTIB ptib;
|
|
DosEnterCritSec();
|
|
DosGetInfoBlocks( &ptib, NULL );
|
|
mutexOwner = ptib->tib_ptib2->tib2_ultid;
|
|
#endif
|
|
assert( !inMutex );
|
|
inMutex = 1;
|
|
}
|
|
void sqlite3Os2LeaveMutex(){
|
|
#ifdef SQLITE_OS2_THREADS
|
|
PTIB ptib;
|
|
#endif
|
|
assert( inMutex );
|
|
inMutex = 0;
|
|
#ifdef SQLITE_OS2_THREADS
|
|
DosGetInfoBlocks( &ptib, NULL );
|
|
assert( mutexOwner == ptib->tib_ptib2->tib2_ultid );
|
|
DosExitCritSec();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** Return TRUE if the mutex is currently held.
|
|
**
|
|
** If the thisThreadOnly parameter is true, return true if and only if the
|
|
** calling thread holds the mutex. If the parameter is false, return
|
|
** true if any thread holds the mutex.
|
|
*/
|
|
int sqlite3Os2InMutex( int thisThreadOnly ){
|
|
#ifdef SQLITE_OS2_THREADS
|
|
PTIB ptib;
|
|
DosGetInfoBlocks( &ptib, NULL );
|
|
return inMutex>0 && (thisThreadOnly==0 || mutexOwner==ptib->tib_ptib2->tib2_ultid);
|
|
#else
|
|
return inMutex>0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** The following variable, if set to a non-zero value, becomes the result
|
|
** returned from sqlite3OsCurrentTime(). This is used for testing.
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
int sqlite3_current_time = 0;
|
|
#endif
|
|
|
|
/*
|
|
** Find the current time (in Universal Coordinated Time). Write the
|
|
** current time and date as a Julian Day number into *prNow and
|
|
** return 0. Return 1 if the time and date cannot be found.
|
|
*/
|
|
int sqlite3Os2CurrentTime( double *prNow ){
|
|
double now;
|
|
USHORT second, minute, hour,
|
|
day, month, year;
|
|
DATETIME dt;
|
|
DosGetDateTime( &dt );
|
|
second = (USHORT)dt.seconds;
|
|
minute = (USHORT)dt.minutes + dt.timezone;
|
|
hour = (USHORT)dt.hours;
|
|
day = (USHORT)dt.day;
|
|
month = (USHORT)dt.month;
|
|
year = (USHORT)dt.year;
|
|
|
|
/* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
|
|
http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */
|
|
/* Calculate the Julian days */
|
|
now = day - 32076 +
|
|
1461*(year + 4800 + (month - 14)/12)/4 +
|
|
367*(month - 2 - (month - 14)/12*12)/12 -
|
|
3*((year + 4900 + (month - 14)/12)/100)/4;
|
|
|
|
/* Add the fractional hours, mins and seconds */
|
|
now += (hour + 12.0)/24.0;
|
|
now += minute/1440.0;
|
|
now += second/86400.0;
|
|
*prNow = now;
|
|
#ifdef SQLITE_TEST
|
|
if( sqlite3_current_time ){
|
|
*prNow = sqlite3_current_time/86400.0 + 2440587.5;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Remember the number of thread-specific-data blocks allocated.
|
|
** Use this to verify that we are not leaking thread-specific-data.
|
|
** Ticket #1601
|
|
*/
|
|
#ifdef SQLITE_TEST
|
|
int sqlite3_tsd_count = 0;
|
|
# define TSD_COUNTER_INCR InterlockedIncrement( &sqlite3_tsd_count )
|
|
# define TSD_COUNTER_DECR InterlockedDecrement( &sqlite3_tsd_count )
|
|
#else
|
|
# define TSD_COUNTER_INCR /* no-op */
|
|
# define TSD_COUNTER_DECR /* no-op */
|
|
#endif
|
|
|
|
/*
|
|
** If called with allocateFlag>1, then return a pointer to thread
|
|
** specific data for the current thread. Allocate and zero the
|
|
** thread-specific data if it does not already exist necessary.
|
|
**
|
|
** If called with allocateFlag==0, then check the current thread
|
|
** specific data. Return it if it exists. If it does not exist,
|
|
** then return NULL.
|
|
**
|
|
** If called with allocateFlag<0, check to see if the thread specific
|
|
** data is allocated and is all zero. If it is then deallocate it.
|
|
** Return a pointer to the thread specific data or NULL if it is
|
|
** unallocated or gets deallocated.
|
|
*/
|
|
ThreadData *sqlite3Os2ThreadSpecificData( int allocateFlag ){
|
|
static ThreadData **s_ppTsd = NULL;
|
|
static const ThreadData zeroData = {0, 0, 0};
|
|
ThreadData *pTsd;
|
|
|
|
if( !s_ppTsd ){
|
|
sqlite3OsEnterMutex();
|
|
if( !s_ppTsd ){
|
|
PULONG pul;
|
|
APIRET rc = DosAllocThreadLocalMemory(1, &pul);
|
|
if( rc != NO_ERROR ){
|
|
sqlite3OsLeaveMutex();
|
|
return 0;
|
|
}
|
|
s_ppTsd = (ThreadData **)pul;
|
|
}
|
|
sqlite3OsLeaveMutex();
|
|
}
|
|
pTsd = *s_ppTsd;
|
|
if( allocateFlag>0 ){
|
|
if( !pTsd ){
|
|
pTsd = sqlite3OsMalloc( sizeof(zeroData) );
|
|
if( pTsd ){
|
|
*pTsd = zeroData;
|
|
*s_ppTsd = pTsd;
|
|
TSD_COUNTER_INCR;
|
|
}
|
|
}
|
|
}else if( pTsd!=0 && allocateFlag<0
|
|
&& memcmp( pTsd, &zeroData, sizeof(ThreadData) )==0 ){
|
|
sqlite3OsFree(pTsd);
|
|
*s_ppTsd = NULL;
|
|
TSD_COUNTER_DECR;
|
|
pTsd = 0;
|
|
}
|
|
return pTsd;
|
|
}
|
|
#endif /* OS_OS2 */
|