/* ** 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 SQLITE_OS_OS2 /* ** A Note About Memory Allocation: ** ** This driver uses malloc()/free() directly rather than going through ** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers ** are designed for use on embedded systems where memory is scarce and ** malloc failures happen frequently. OS/2 does not typically run on ** embedded systems, and when it does the developers normally have bigger ** problems to worry about than running out of memory. So there is not ** a compelling need to use the wrappers. ** ** But there is a good reason to not use the wrappers. If we use the ** wrappers then we will get simulated malloc() failures within this ** driver. And that causes all kinds of problems for our tests. We ** could enhance SQLite to deal with simulated malloc failures within ** the OS driver, but the code to deal with those failure would not ** be exercised on Linux (which does not need to malloc() in the driver) ** and so we would have difficulty writing coverage tests for that ** code. Better to leave the code out, we think. ** ** The point of this discussion is as follows: When creating a new ** OS layer for an embedded system, if you use this file as an example, ** avoid the use of malloc()/free(). Those routines work ok on OS/2 ** desktops but not so well in embedded systems. */ /* ** Macros used to determine whether or not to use threads. */ #if defined(SQLITE_THREADSAFE) && SQLITE_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 sqlite3_file specific for the OS/2 ** protability layer. */ typedef struct os2File os2File; struct os2File { const sqlite3_io_methods *pMethod; /* Always the first entry */ HFILE h; /* Handle for accessing the file */ char* pathToDel; /* Name of file to delete on close, NULL if not */ unsigned char locktype; /* Type of lock currently held on this file */ }; #define LOCK_TIMEOUT 10L /* the default locking timeout */ /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. ******************************************************************************/ /* ** Close a file. */ static int os2Close( sqlite3_file *id ){ APIRET rc = NO_ERROR; os2File *pFile; if( id && (pFile = (os2File*)id) != 0 ){ OSTRACE(( "CLOSE %d\n", pFile->h )); rc = DosClose( pFile->h ); pFile->locktype = NO_LOCK; if( pFile->pathToDel != NULL ){ rc = DosForceDelete( (PSZ)pFile->pathToDel ); free( pFile->pathToDel ); pFile->pathToDel = NULL; } id = 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. */ static int os2Read( sqlite3_file *id, /* File to read from */ void *pBuf, /* Write content into this buffer */ int amt, /* Number of bytes to read */ sqlite3_int64 offset /* Begin reading at this offset */ ){ ULONG fileLocation = 0L; ULONG got; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_READ ); OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype )); if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ return SQLITE_IOERR; } if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){ return SQLITE_IOERR_READ; } if( got == (ULONG)amt ) return SQLITE_OK; else { /* Unread portions of the input buffer must be zero-filled */ 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. */ static int os2Write( sqlite3_file *id, /* File to write into */ const void *pBuf, /* The bytes to be written */ int amt, /* Number of bytes to write */ sqlite3_int64 offset /* Offset into the file to begin writing at */ ){ ULONG fileLocation = 0L; APIRET rc = NO_ERROR; ULONG wrote; os2File *pFile = (os2File*)id; assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_WRITE ); SimulateDiskfullError( return SQLITE_FULL ); OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype )); if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ return SQLITE_IOERR; } assert( amt>0 ); while( amt > 0 && ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR && wrote > 0 ){ amt -= wrote; pBuf = &((char*)pBuf)[wrote]; } return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK; } /* ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ APIRET rc = NO_ERROR; os2File *pFile = (os2File*)id; OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte )); SimulateIOError( return SQLITE_IOERR_TRUNCATE ); rc = DosSetFileSize( pFile->h, nByte ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } #ifdef SQLITE_TEST /* ** Count the number of fullsyncs and normal syncs. This is used to test ** that syncs and fullsyncs are occuring at the right times. */ int sqlite3_sync_count = 0; int sqlite3_fullsync_count = 0; #endif /* ** Make sure all writes to a particular file are committed to disk. */ static int os2Sync( sqlite3_file *id, int flags ){ os2File *pFile = (os2File*)id; OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype )); #ifdef SQLITE_TEST if( flags & SQLITE_SYNC_FULL){ sqlite3_fullsync_count++; } sqlite3_sync_count++; #endif /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a ** no-op */ #ifdef SQLITE_NO_SYNC UNUSED_PARAMETER(pFile); return SQLITE_OK; #else return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR; #endif } /* ** Determine the current size of a file in bytes */ static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){ APIRET rc = NO_ERROR; FILESTATUS3 fsts3FileInfo; memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo)); assert( id!=0 ); SimulateIOError( return SQLITE_IOERR_FSTAT ); rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) ); if( rc == NO_ERROR ){ *pSize = fsts3FileInfo.cbFile; return SQLITE_OK; }else{ return SQLITE_IOERR_FSTAT; } } /* ** Acquire a reader lock. */ static int getReadLock( os2File *pFile ){ FILELOCK LockArea, UnlockArea; APIRET res; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = SHARED_FIRST; LockArea.lRange = SHARED_SIZE; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res )); return res; } /* ** Undo a readlock */ static int unlockReadLock( os2File *id ){ FILELOCK LockArea, UnlockArea; APIRET res; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = SHARED_FIRST; UnlockArea.lRange = SHARED_SIZE; res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res )); return res; } /* ** 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. */ static int os2Lock( sqlite3_file *id, int locktype ){ int rc = SQLITE_OK; /* Return code from subroutines */ APIRET res = NO_ERROR; /* Result of an OS/2 lock call */ int newLocktype; /* Set pFile->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 ); OSTRACE(( "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 ** os2File, do nothing. Don't use the end_lock: exit path, as ** sqlite3_mutex_enter() hasn't been called yet. */ if( pFile->locktype>=locktype ){ OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, 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) ){ LockArea.lOffset = PENDING_BYTE; LockArea.lRange = 1L; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */ res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L ); if( res == NO_ERROR ){ gotPendingLock = 1; OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res )); } } /* Acquire a shared lock */ if( locktype==SHARED_LOCK && res == NO_ERROR ){ assert( pFile->locktype==NO_LOCK ); res = getReadLock(pFile); if( res == NO_ERROR ){ newLocktype = SHARED_LOCK; } OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res )); } /* Acquire a RESERVED lock */ if( locktype==RESERVED_LOCK && res == NO_ERROR ){ assert( pFile->locktype==SHARED_LOCK ); LockArea.lOffset = RESERVED_BYTE; LockArea.lRange = 1L; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); if( res == NO_ERROR ){ newLocktype = RESERVED_LOCK; } OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res )); } /* Acquire a PENDING lock */ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ newLocktype = PENDING_LOCK; gotPendingLock = 0; OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n", pFile->h )); } /* Acquire an EXCLUSIVE lock */ if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); OSTRACE(( "unreadlock = %d\n", res )); LockArea.lOffset = SHARED_FIRST; LockArea.lRange = SHARED_SIZE; UnlockArea.lOffset = 0L; UnlockArea.lRange = 0L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); if( res == NO_ERROR ){ newLocktype = EXCLUSIVE_LOCK; }else{ OSTRACE(( "OS/2 error-code = %d\n", res )); getReadLock(pFile); } OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res )); } /* If we are holding a PENDING lock that ought to be released, then ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ int r; LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = PENDING_BYTE; UnlockArea.lRange = 1L; r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r )); } /* 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{ OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype )); rc = SQLITE_BUSY; } pFile->locktype = newLocktype; OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype )); 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. */ static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){ int r = 0; os2File *pFile = (os2File*)id; assert( pFile!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ r = 1; OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r )); }else{ FILELOCK LockArea, UnlockArea; APIRET rc = NO_ERROR; 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, LOCK_TIMEOUT, 0L ); OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc )); if( rc == NO_ERROR ){ APIRET rcu = NO_ERROR; /* return code for unlocking */ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = RESERVED_BYTE; UnlockArea.lRange = 1L; rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu )); } r = !(rc == NO_ERROR); OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r )); } *pOut = r; return SQLITE_OK; } /* ** 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; */ static int os2Unlock( sqlite3_file *id, int locktype ){ int type; os2File *pFile = (os2File*)id; APIRET rc = SQLITE_OK; APIRET res = NO_ERROR; FILELOCK LockArea, UnlockArea; memset(&LockArea, 0, sizeof(LockArea)); memset(&UnlockArea, 0, sizeof(UnlockArea)); assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); OSTRACE(( "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; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res )); if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){ /* This should never happen. We should always be able to ** reacquire the read lock */ OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype )); rc = SQLITE_IOERR_UNLOCK; } } if( type>=RESERVED_LOCK ){ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = RESERVED_BYTE; UnlockArea.lRange = 1L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res )); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ res = unlockReadLock(pFile); OSTRACE(( "UNLOCK %d is %d want %d res=%d\n", pFile->h, type, locktype, res )); } if( type>=PENDING_LOCK ){ LockArea.lOffset = 0L; LockArea.lRange = 0L; UnlockArea.lOffset = PENDING_BYTE; UnlockArea.lRange = 1L; res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res )); } pFile->locktype = locktype; OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype )); return rc; } /* ** Control and query of the open file handle. */ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((os2File*)id)->locktype; OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype )); return SQLITE_OK; } } return SQLITE_ERROR; } /* ** 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 its journal file) that the sector size will be the ** same for both. */ static int os2SectorSize(sqlite3_file *id){ return SQLITE_DEFAULT_SECTOR_SIZE; } /* ** Return a vector of device characteristics. */ static int os2DeviceCharacteristics(sqlite3_file *id){ return 0; } /* ** Character set conversion objects used by conversion routines. */ static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */ static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */ /* ** Helper function to initialize the conversion objects from and to UTF-8. */ static void initUconvObjects( void ){ if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS ) ucUtf8 = NULL; if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS ) uclCp = NULL; } /* ** Helper function to free the conversion objects from and to UTF-8. */ static void freeUconvObjects( void ){ if ( ucUtf8 ) UniFreeUconvObject( ucUtf8 ); if ( uclCp ) UniFreeUconvObject( uclCp ); ucUtf8 = NULL; uclCp = NULL; } /* ** Helper function to convert UTF-8 filenames to local OS/2 codepage. ** The two-step process: first convert the incoming UTF-8 string ** into UCS-2 and then from UCS-2 to the current codepage. ** The returned char pointer has to be freed. */ static char *convertUtf8PathToCp( const char *in ){ UniChar tempPath[CCHMAXPATH]; char *out = (char *)calloc( CCHMAXPATH, 1 ); if( !out ) return NULL; if( !ucUtf8 || !uclCp ) initUconvObjects(); /* determine string for the conversion of UTF-8 which is CP1208 */ if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS ) return out; /* if conversion fails, return the empty string */ /* conversion for current codepage which can be used for paths */ UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH ); return out; } /* ** Helper function to convert filenames from local codepage to UTF-8. ** The two-step process: first convert the incoming codepage-specific ** string into UCS-2 and then from UCS-2 to the codepage of UTF-8. ** The returned char pointer has to be freed. ** ** This function is non-static to be able to use this in shell.c and ** similar applications that take command line arguments. */ char *convertCpPathToUtf8( const char *in ){ UniChar tempPath[CCHMAXPATH]; char *out = (char *)calloc( CCHMAXPATH, 1 ); if( !out ) return NULL; if( !ucUtf8 || !uclCp ) initUconvObjects(); /* conversion for current codepage which can be used for paths */ if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS ) return out; /* if conversion fails, return the empty string */ /* determine string for the conversion of UTF-8 which is CP1208 */ UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH ); return out; } /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { 1, /* iVersion */ os2Close, os2Read, os2Write, os2Truncate, os2Sync, os2FileSize, os2Lock, os2Unlock, os2CheckReservedLock, os2FileControl, os2SectorSize, os2DeviceCharacteristics }; /*************************************************************************** ** Here ends the I/O methods that form the sqlite3_io_methods object. ** ** The next block of code implements the VFS methods. ****************************************************************************/ /* ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at pVfs->mxPathname characters. */ static int getTempname(int nBuf, char *zBuf ){ static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; char zTempPathBuf[3]; PSZ zTempPath = (PSZ)&zTempPathBuf; if( sqlite3_temp_directory ){ zTempPath = sqlite3_temp_directory; }else{ 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. * * Trailing spaces are not allowed, either. */ j = sqlite3Strlen30(zTempPath); while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' || zTempPath[j-1] == ' ' ) ){ j--; } zTempPath[j] = '\0'; if( !sqlite3_temp_directory ){ char *zTempPathUTF = convertCpPathToUtf8( zTempPath ); sqlite3_snprintf( nBuf-30, zBuf, "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF ); free( zTempPathUTF ); }else{ sqlite3_snprintf( nBuf-30, zBuf, "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath ); } j = sqlite3Strlen30( zBuf ); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); return SQLITE_OK; } /* ** Turn a relative pathname into a full pathname. Write the full ** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname ** bytes in size. */ static int os2FullPathname( sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zRelative, /* Possibly relative input path */ int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ char *zRelativeCp = convertUtf8PathToCp( zRelative ); char zFullCp[CCHMAXPATH] = "\0"; char *zFullUTF; APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp, CCHMAXPATH ); free( zRelativeCp ); zFullUTF = convertCpPathToUtf8( zFullCp ); sqlite3_snprintf( nFull, zFull, zFullUTF ); free( zFullUTF ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR; } /* ** Open a file. */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ const char *zName, /* Name of the file */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HFILE h; ULONG ulFileAttribute = FILE_NORMAL; ULONG ulOpenFlags = 0; ULONG ulOpenMode = 0; os2File *pFile = (os2File*)id; APIRET rc = NO_ERROR; ULONG ulAction; char *zNameCp; char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */ /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zName ){ int rc = getTempname(CCHMAXPATH+1, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zName = zTmpname; } memset( pFile, 0, sizeof(*pFile) ); OSTRACE(( "OPEN want %d\n", flags )); if( flags & SQLITE_OPEN_READWRITE ){ ulOpenMode |= OPEN_ACCESS_READWRITE; OSTRACE(( "OPEN read/write\n" )); }else{ ulOpenMode |= OPEN_ACCESS_READONLY; OSTRACE(( "OPEN read only\n" )); } if( flags & SQLITE_OPEN_CREATE ){ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; OSTRACE(( "OPEN open new/create\n" )); }else{ ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; OSTRACE(( "OPEN open existing\n" )); } if( flags & SQLITE_OPEN_MAIN_DB ){ ulOpenMode |= OPEN_SHARE_DENYNONE; OSTRACE(( "OPEN share read/write\n" )); }else{ ulOpenMode |= OPEN_SHARE_DENYWRITE; OSTRACE(( "OPEN share read only\n" )); } if( flags & SQLITE_OPEN_DELETEONCLOSE ){ char pathUtf8[CCHMAXPATH]; #ifdef NDEBUG /* when debugging we want to make sure it is deleted */ ulFileAttribute = FILE_HIDDEN; #endif os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); OSTRACE(( "OPEN hidden/delete on close file attributes\n" )); }else{ pFile->pathToDel = NULL; OSTRACE(( "OPEN normal file attribute\n" )); } /* always open in random access mode for possibly better speed */ ulOpenMode |= OPEN_FLAGS_RANDOM; ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; ulOpenMode |= OPEN_FLAGS_NOINHERIT; zNameCp = convertUtf8PathToCp( zName ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, ulFileAttribute, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); if( rc != NO_ERROR ){ OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode )); if( pFile->pathToDel ) free( pFile->pathToDel ); pFile->pathToDel = NULL; if( flags & SQLITE_OPEN_READWRITE ){ OSTRACE(( "OPEN %d Invalid handle\n", ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) )); return os2Open( pVfs, zName, id, ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE), pOutFlags ); }else{ return SQLITE_CANTOPEN; } } if( pOutFlags ){ *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } pFile->pMethod = &os2IoMethod; pFile->h = h; OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; } /* ** Delete the named file. */ static int os2Delete( sqlite3_vfs *pVfs, /* Not used on os2 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ APIRET rc = NO_ERROR; char *zFilenameCp = convertUtf8PathToCp( zFilename ); SimulateIOError( return SQLITE_IOERR_DELETE ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* ** Check the existance and status of a file. */ static int os2Access( sqlite3_vfs *pVfs, /* Not used on os2 */ const char *zFilename, /* Name of file to check */ int flags, /* Type of test to make on this file */ int *pOut /* Write results here */ ){ FILESTATUS3 fsts3ConfigInfo; APIRET rc = NO_ERROR; char *zFilenameCp = convertUtf8PathToCp( zFilename ); memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) ); free( zFilenameCp ); OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", fsts3ConfigInfo.attrFile, flags, rc )); switch( flags ){ case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: rc = (rc == NO_ERROR); OSTRACE(( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc)); break; case SQLITE_ACCESS_READWRITE: rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); OSTRACE(( "ACCESS %s access of read/write rc=%d\n", zFilename, rc )); break; default: assert( !"Invalid flags argument" ); } *pOut = rc; return SQLITE_OK; } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Interfaces for opening a shared library, finding entry points ** within the shared library, and closing the shared library. */ /* ** Interfaces for opening a shared library, finding entry points ** within the shared library, and closing the shared library. */ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ UCHAR loadErr[256]; HMODULE hmod; APIRET rc; char *zFilenameCp = convertUtf8PathToCp(zFilename); rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod); free(zFilenameCp); return rc != NO_ERROR ? 0 : (void*)hmod; } /* ** A no-op since the error code is returned on the DosLoadModule call. ** os2Dlopen returns zero if DosLoadModule is not successful. */ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } static void *os2DlSym(sqlite3_vfs *pVfs, 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); } return rc != NO_ERROR ? 0 : (void*)pfn; } static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ DosFreeModule((HMODULE)pHandle); } #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ #define os2DlOpen 0 #define os2DlError 0 #define os2DlSym 0 #define os2DlClose 0 #endif /* ** Write up to nBuf bytes of randomness into zBuf. */ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){ int n = 0; #if defined(SQLITE_TEST) n = nBuf; memset(zBuf, 0, nBuf); #else int sizeofULong = sizeof(ULONG); if( (int)sizeof(DATETIME) <= nBuf - n ){ DATETIME x; DosGetDateTime(&x); memcpy(&zBuf[n], &x, sizeof(x)); n += sizeof(x); } if( sizeofULong <= nBuf - n ){ PPIB ppib; DosGetInfoBlocks(NULL, &ppib); memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong); n += sizeofULong; } if( sizeofULong <= nBuf - n ){ PTIB ptib; DosGetInfoBlocks(&ptib, NULL); memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong); n += sizeofULong; } /* if we still haven't filled the buffer yet the following will */ /* grab everything once instead of making several calls for a single item */ if( sizeofULong <= nBuf - n ){ ULONG ulSysInfo[QSV_MAX]; DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX); memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong); n += sizeofULong; if( sizeofULong <= nBuf - n ){ memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong); n += sizeofULong; } if( sizeofULong <= nBuf - n ){ memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong); n += sizeofULong; } if( sizeofULong <= nBuf - n ){ memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong); n += sizeofULong; } if( sizeofULong <= nBuf - n ){ memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong); n += sizeofULong; } } #endif return n; } /* ** Sleep for a little while. Return the amount of time slept. ** The argument is the number of microseconds we want to sleep. ** The return value is the number of microseconds of sleep actually ** requested from the underlying operating system, a number which ** might be greater than or equal to the argument, but not less ** than the argument. */ static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){ DosSleep( (microsec/1000) ); return microsec; } /* ** 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 os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ double now; SHORT minute; /* needs to be able to cope with negative timezone offset */ USHORT second, hour, day, month, year; DATETIME dt; DosGetDateTime( &dt ); second = (USHORT)dt.seconds; minute = (SHORT)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; } static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ return 0; } /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs os2Vfs = { 1, /* iVersion */ sizeof(os2File), /* szOsFile */ CCHMAXPATH, /* mxPathname */ 0, /* pNext */ "os2", /* zName */ 0, /* pAppData */ os2Open, /* xOpen */ os2Delete, /* xDelete */ os2Access, /* xAccess */ os2FullPathname, /* xFullPathname */ os2DlOpen, /* xDlOpen */ os2DlError, /* xDlError */ os2DlSym, /* xDlSym */ os2DlClose, /* xDlClose */ os2Randomness, /* xRandomness */ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); return SQLITE_OK; } int sqlite3_os_end(void){ freeUconvObjects(); return SQLITE_OK; } #endif /* SQLITE_OS_OS2 */