/* ** 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. */ #if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY) /* os2safe.h has to be included before os2.h, needed for high mem */ #include #endif #include "sqliteInt.h" #include "os.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 ); TRACE2( "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); TRACE3( "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( sqlite3OsFullPathname( zFilename ) ); TRACE3( "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 ); TRACE3( "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; } /* ** If the following global variable points to a string which is the ** name of a directory, then that directory will be used to store ** temporary files. */ char *sqlite3_temp_directory = 0; /* ** 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( "TEMP", &zTempPath ) ){ if( DosScanEnv( "TMP", &zTempPath ) ){ if( DosScanEnv( "TMPDIR", &zTempPath ) ){ ULONG ulDriveNum = 0, ulDriveMap = 0; DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); sprintf( zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); } } } 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; } TRACE2( "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 ){ TRACE2( "CLOSE %d\n", pFile->h ); rc = DosClose( pFile->h ); pFile->locktype = NO_LOCK; if( pFile->delOnClose != 0 ){ rc = DosForceDelete( 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 ); TRACE3( "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 ); TRACE3( "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 ); TRACE3( "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 ); TRACE3( "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 ); TRACE3( "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 ); TRACE4( "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. */ TRACE2( "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); TRACE2( "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{ TRACE2( "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{ TRACE4( "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; TRACE3( "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 ); } TRACE3( "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 ); TRACE4( "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{ char zBuff[SQLITE_TEMPNAME_SIZE - 2] = {0}; char zDrive[1] = {0}; ULONG cbzFullLen = SQLITE_TEMPNAME_SIZE; ULONG ulDriveNum = 0; ULONG ulDriveMap = 0; DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); DosQueryCurrentDir( 0L, zBuff, &cbzFullLen ); zFull = sqliteMalloc( cbzFullLen ); sprintf( zDrive, "%c", (char)('A' + ulDriveNum - 1) ); sqlite3SetString( &zFull, zDrive, ":\\", zBuff, "\\", zRelative, (char*)0 ); } 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; } /* ** 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, }; /* ** 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(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 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(){ PTIB ptib; #ifdef SQLITE_OS2_THREADS DosEnterCritSec(); DosGetInfoBlocks( &ptib, NULL ); mutexOwner = ptib->tib_ptib2->tib2_ultid; #endif assert( !inMutex ); inMutex = 1; } void sqlite3Os2LeaveMutex(){ PTIB ptib; 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 */