/* * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ * Version 1.1.6 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://unqlite.org/licensing.html */ /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel $ */ #ifndef UNQLITE_AMALGAMATION #include "unqliteInt.h" #endif /* Omit the whole layer from the build if compiling for platforms other than Windows */ #ifdef __WINNT__ /* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */ #include /* ** Some microsoft compilers lack this definition. */ #ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif /* ** WinCE lacks native support for file locking so we have to fake it ** with some code of our own. */ #ifdef __WIN_CE__ typedef struct winceLock { int nReaders; /* Number of reader locks obtained */ BOOL bPending; /* Indicates a pending lock has been obtained */ BOOL bReserved; /* Indicates a reserved lock has been obtained */ BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ } winceLock; #define AreFileApisANSI() 1 #define FormatMessageW(a,b,c,d,e,f,g) 0 #endif /* ** The winFile structure is a subclass of unqlite_file* specific to the win32 ** portability layer. */ typedef struct winFile winFile; struct winFile { const unqlite_io_methods *pMethod; /*** Must be first ***/ unqlite_vfs *pVfs; /* The VFS used to open this file */ HANDLE h; /* Handle for accessing the file */ sxu8 locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ int szChunk; /* Chunk size */ #ifdef __WIN_CE__ WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ HANDLE hShared; /* Shared memory segment used for locking */ winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif }; /* ** Convert a UTF-8 string to microsoft unicode (UTF-16?). ** ** Space to hold the returned string is obtained from HeapAlloc(). */ static WCHAR *utf8ToUnicode(const char *zFilename){ int nChar; WCHAR *zWideFilename; nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) ); if( zWideFilename==0 ){ return 0; } nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); if( nChar==0 ){ HeapFree(GetProcessHeap(),0,zWideFilename); zWideFilename = 0; } return zWideFilename; } /* ** Convert microsoft unicode to UTF-8. Space to hold the returned string is ** obtained from malloc(). */ static char *unicodeToUtf8(const WCHAR *zWideFilename){ int nByte; char *zFilename; nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte ); if( zFilename==0 ){ return 0; } nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0); if( nByte == 0 ){ HeapFree(GetProcessHeap(),0,zFilename); zFilename = 0; } return zFilename; } /* ** Convert an ansi string to microsoft unicode, based on the ** current codepage settings for file apis. ** ** Space to hold the returned string is obtained ** from malloc. */ static WCHAR *mbcsToUnicode(const char *zFilename){ int nByte; WCHAR *zMbcsFilename; int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR); zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) ); if( zMbcsFilename==0 ){ return 0; } nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); if( nByte==0 ){ HeapFree(GetProcessHeap(),0,zMbcsFilename); zMbcsFilename = 0; } return zMbcsFilename; } /* ** Convert multibyte character string to UTF-8. Space to hold the ** returned string is obtained from malloc(). */ char *unqlite_win32_mbcs_to_utf8(const char *zFilename){ char *zFilenameUtf8; WCHAR *zTmpWide; zTmpWide = mbcsToUnicode(zFilename); if( zTmpWide==0 ){ return 0; } zFilenameUtf8 = unicodeToUtf8(zTmpWide); HeapFree(GetProcessHeap(),0,zTmpWide); return zFilenameUtf8; } /* ** Some microsoft compilers lack this definition. */ #ifndef INVALID_SET_FILE_POINTER # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif /* ** Move the current position of the file handle passed as the first ** argument to offset iOffset within the file. If successful, return 0. ** Otherwise, set pFile->lastErrno and return non-zero. */ static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){ LONG upperBits; /* Most sig. 32 bits of new offset */ LONG lowerBits; /* Least sig. 32 bits of new offset */ DWORD dwRet; /* Value returned by SetFilePointer() */ upperBits = (LONG)((iOffset>>32) & 0x7fffffff); lowerBits = (LONG)(iOffset & 0xffffffff); /* API oddity: If successful, SetFilePointer() returns a dword ** containing the lower 32-bits of the new file-offset. Or, if it fails, ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine ** whether an error has actually occured, it is also necessary to call ** GetLastError(). */ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ pFile->lastErrno = GetLastError(); return 1; } return 0; } /* ** Close a file. ** ** It is reported that an attempt to close a handle might sometimes ** fail. This is a very unreasonable result, but windows is notorious ** for being unreasonable so I do not doubt that it might happen. If ** the close fails, we pause for 100 milliseconds and try again. As ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before ** giving up and returning an error. */ #define MX_CLOSE_ATTEMPT 3 static int winClose(unqlite_file *id) { int rc, cnt = 0; winFile *pFile = (winFile*)id; do{ rc = CloseHandle(pFile->h); }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); return rc ? UNQLITE_OK : UNQLITE_IOERR; } /* ** Read data from a file into a buffer. Return UNQLITE_OK if all ** bytes were read successfully and UNQLITE_IOERR if anything goes ** wrong. */ static int winRead( unqlite_file *id, /* File to read from */ void *pBuf, /* Write content into this buffer */ unqlite_int64 amt, /* Number of bytes to read */ unqlite_int64 offset /* Begin reading at this offset */ ){ winFile *pFile = (winFile*)id; /* file handle */ DWORD nRead; /* Number of bytes actually read from file */ if( seekWinFile(pFile, offset) ){ return UNQLITE_FULL; } if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){ pFile->lastErrno = GetLastError(); return UNQLITE_IOERR; } if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead)); return UNQLITE_IOERR; } return UNQLITE_OK; } /* ** Write data from a buffer into a file. Return UNQLITE_OK on success ** or some other error code on failure. */ static int winWrite( unqlite_file *id, /* File to write into */ const void *pBuf, /* The bytes to be written */ unqlite_int64 amt, /* Number of bytes to write */ unqlite_int64 offset /* Offset into the file to begin writing at */ ){ int rc; /* True if error has occured, else false */ winFile *pFile = (winFile*)id; /* File handle */ rc = seekWinFile(pFile, offset); if( rc==0 ){ sxu8 *aRem = (sxu8 *)pBuf; /* Data yet to be written */ unqlite_int64 nRem = amt; /* Number of bytes yet to be written */ DWORD nWrite; /* Bytes written by each WriteFile() call */ while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){ aRem += nWrite; nRem -= nWrite; } if( nRem>0 ){ pFile->lastErrno = GetLastError(); rc = 1; } } if( rc ){ if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return UNQLITE_FULL; } return UNQLITE_IOERR; } return UNQLITE_OK; } /* ** Truncate an open file to a specified size */ static int winTruncate(unqlite_file *id, unqlite_int64 nByte){ winFile *pFile = (winFile*)id; /* File handle object */ int rc = UNQLITE_OK; /* Return code for this function */ /* If the user has configured a chunk-size for this file, truncate the ** file so that it consists of an integer number of chunks (i.e. the ** actual file size after the operation may be larger than the requested ** size). */ if( pFile->szChunk ){ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ rc = UNQLITE_IOERR; }else if( 0==SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); rc = UNQLITE_IOERR; } return rc; } /* ** Make sure all writes to a particular file are committed to disk. */ static int winSync(unqlite_file *id, int flags){ winFile *pFile = (winFile*)id; SXUNUSED(flags); /* MSVC warning */ if( FlushFileBuffers(pFile->h) ){ return UNQLITE_OK; }else{ pFile->lastErrno = GetLastError(); return UNQLITE_IOERR; } } /* ** Determine the current size of a file in bytes */ static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){ DWORD upperBits; DWORD lowerBits; winFile *pFile = (winFile*)id; DWORD error; lowerBits = GetFileSize(pFile->h, &upperBits); if( (lowerBits == INVALID_FILE_SIZE) && ((error = GetLastError()) != NO_ERROR) ) { pFile->lastErrno = error; return UNQLITE_IOERR; } *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits; return UNQLITE_OK; } /* ** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ #ifndef LOCKFILE_FAIL_IMMEDIATELY # define LOCKFILE_FAIL_IMMEDIATELY 1 #endif /* ** Acquire a reader lock. */ static int getReadLock(winFile *pFile){ int res; OVERLAPPED ovlp; ovlp.Offset = SHARED_FIRST; ovlp.OffsetHigh = 0; ovlp.hEvent = 0; res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp); if( res == 0 ){ pFile->lastErrno = GetLastError(); } return res; } /* ** Undo a readlock */ static int unlockReadLock(winFile *pFile){ int res; res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res == 0 ){ pFile->lastErrno = GetLastError(); } 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 winUnlock() 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 winLock(unqlite_file *id, int locktype){ int rc = UNQLITE_OK; /* Return code from subroutines */ int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set pFile->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ winFile *pFile = (winFile*)id; DWORD error = NO_ERROR; /* If there is already a lock of this type or more restrictive on the ** OsFile, do nothing. */ if( pFile->locktype>=locktype ){ return UNQLITE_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; while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ /* Try 3 times to get the pending lock. The pending lock might be ** held by another reader process who will release it momentarily. */ Sleep(1); } gotPendingLock = res; if( !res ){ error = GetLastError(); } } /* Acquire a shared lock */ if( locktype==SHARED_LOCK && res ){ /* assert( pFile->locktype==NO_LOCK ); */ res = getReadLock(pFile); if( res ){ newLocktype = SHARED_LOCK; }else{ error = GetLastError(); } } /* Acquire a RESERVED lock */ if( locktype==RESERVED_LOCK && res ){ /* assert( pFile->locktype==SHARED_LOCK ); */ res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( res ){ newLocktype = RESERVED_LOCK; }else{ error = GetLastError(); } } /* 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); res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ error = GetLastError(); getReadLock(pFile); } } /* If we are holding a PENDING lock that ought to be released, then ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } /* Update the state of the lock has held in the file descriptor then ** return the appropriate result code. */ if( res ){ rc = UNQLITE_OK; }else{ pFile->lastErrno = error; rc = UNQLITE_BUSY; } pFile->locktype = (sxu8)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. */ static int winCheckReservedLock(unqlite_file *id, int *pResOut){ int rc; winFile *pFile = (winFile*)id; if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; }else{ rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( rc ){ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } rc = !rc; } *pResOut = rc; return UNQLITE_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 UNQLITE_IOERR; */ static int winUnlock(unqlite_file *id, int locktype){ int type; winFile *pFile = (winFile*)id; int rc = UNQLITE_OK; type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ rc = UNQLITE_IOERR; } } if( type>=RESERVED_LOCK ){ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ unlockReadLock(pFile); } if( type>=PENDING_LOCK ){ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } pFile->locktype = (sxu8)locktype; return rc; } /* ** 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. ** */ static int winSectorSize(unqlite_file *id){ return (int)(((winFile*)id)->sectorSize); } /* ** This vector defines all the methods that can operate on an ** unqlite_file for Windows systems. */ static const unqlite_io_methods winIoMethod = { 1, /* iVersion */ winClose, /* xClose */ winRead, /* xRead */ winWrite, /* xWrite */ winTruncate, /* xTruncate */ winSync, /* xSync */ winFileSize, /* xFileSize */ winLock, /* xLock */ winUnlock, /* xUnlock */ winCheckReservedLock, /* xCheckReservedLock */ winSectorSize, /* xSectorSize */ }; /* * Windows VFS Methods. */ /* ** Convert a UTF-8 filename into whatever form the underlying ** operating system wants filenames in. Space to hold the result ** is obtained from malloc and must be freed by the calling ** function. */ static void *convertUtf8Filename(const char *zFilename) { void *zConverted; zConverted = utf8ToUnicode(zFilename); /* caller will handle out of memory */ return zConverted; } /* ** Delete the named file. ** ** Note that windows does not allow a file to be deleted if some other ** process has it open. Sometimes a virus scanner or indexing program ** will open a journal file shortly after it is created in order to do ** whatever it does. While this other process is holding the ** file open, we will be unable to delete it. To work around this ** problem, we delay 100 milliseconds and try to delete again. Up ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving ** up and returning an error. */ #define MX_DELETION_ATTEMPTS 5 static int winDelete( unqlite_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on win32 */ ){ int cnt = 0; DWORD rc; DWORD error = 0; void *zConverted; zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ SXUNUSED(pVfs); SXUNUSED(syncDir); return UNQLITE_NOMEM; } do{ DeleteFileW((LPCWSTR)zConverted); }while( ( ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES) || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) && (++cnt < MX_DELETION_ATTEMPTS) && (Sleep(100), 1) ); HeapFree(GetProcessHeap(),0,zConverted); return ( (rc == INVALID_FILE_ATTRIBUTES) && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR; } /* ** Check the existance and status of a file. */ static int winAccess( unqlite_vfs *pVfs, /* Not used */ const char *zFilename, /* Name of file to check */ int flags, /* Type of test to make on this file */ int *pResOut /* OUT: Result */ ){ WIN32_FILE_ATTRIBUTE_DATA sAttrData; DWORD attr; int rc = 0; void *zConverted; SXUNUSED(pVfs); zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ return UNQLITE_NOMEM; } SyZero(&sAttrData,sizeof(sAttrData)); if( GetFileAttributesExW((WCHAR*)zConverted, GetFileExInfoStandard, &sAttrData) ){ /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file ** as if it does not exist. */ if( flags==UNQLITE_ACCESS_EXISTS && sAttrData.nFileSizeHigh==0 && sAttrData.nFileSizeLow==0 ){ attr = INVALID_FILE_ATTRIBUTES; }else{ attr = sAttrData.dwFileAttributes; } }else{ if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ HeapFree(GetProcessHeap(),0,zConverted); return UNQLITE_IOERR; }else{ attr = INVALID_FILE_ATTRIBUTES; } } HeapFree(GetProcessHeap(),0,zConverted); switch( flags ){ case UNQLITE_ACCESS_READWRITE: rc = (attr & FILE_ATTRIBUTE_READONLY)==0; break; case UNQLITE_ACCESS_READ: case UNQLITE_ACCESS_EXISTS: default: rc = attr!=INVALID_FILE_ATTRIBUTES; break; } *pResOut = rc; return UNQLITE_OK; } /* ** Turn a relative pathname into a full pathname. Write the full ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname ** bytes in size. */ static int winFullPathname( unqlite_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 */ ){ int nByte; void *zConverted; WCHAR *zTemp; char *zOut; SXUNUSED(nFull); zConverted = convertUtf8Filename(zRelative); if( zConverted == 0 ){ return UNQLITE_NOMEM; } nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ HeapFree(GetProcessHeap(),0,zConverted); return UNQLITE_NOMEM; } GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); HeapFree(GetProcessHeap(),0,zConverted); zOut = unicodeToUtf8(zTemp); HeapFree(GetProcessHeap(),0,zTemp); if( zOut == 0 ){ return UNQLITE_NOMEM; } Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0); HeapFree(GetProcessHeap(),0,zOut); return UNQLITE_OK; } /* ** Get the sector size of the device used to store ** file. */ static int getSectorSize( unqlite_vfs *pVfs, const char *zRelative /* UTF-8 file name */ ){ DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; char zFullpath[MAX_PATH+1]; int rc; DWORD dwRet = 0; DWORD dwDummy; /* ** We need to get the full path name of the file ** to get the drive letter to look up the sector ** size. */ rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); if( rc == UNQLITE_OK ) { void *zConverted = convertUtf8Filename(zFullpath); if( zConverted ){ /* trim path to just drive reference */ WCHAR *p = (WCHAR *)zConverted; for(;*p;p++){ if( *p == '\\' ){ *p = '\0'; break; } } dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy); HeapFree(GetProcessHeap(),0,zConverted); } if( !dwRet ){ bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; } } return (int) bytesPerSector; } /* ** Sleep for a little while. Return the amount of time slept. */ static int winSleep(unqlite_vfs *pVfs, int microsec){ Sleep((microsec+999)/1000); SXUNUSED(pVfs); return ((microsec+999)/1000)*1000; } /* * Export the current system time. */ static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut) { SYSTEMTIME sSys; SXUNUSED(pVfs); GetSystemTime(&sSys); SYSTEMTIME_TO_SYTM(&sSys,pOut); return UNQLITE_OK; } /* ** The idea is that this function works like a combination of ** GetLastError() and FormatMessage() on windows (or errno and ** strerror_r() on unix). After an error is returned by an OS ** function, UnQLite calls this function with zBuf pointing to ** a buffer of nBuf bytes. The OS layer should populate the ** buffer with a nul-terminated UTF-8 encoded error message ** describing the last IO error to have occurred within the calling ** thread. ** ** If the error message is too large for the supplied buffer, ** it should be truncated. The return value of xGetLastError ** is zero if the error message fits in the buffer, or non-zero ** otherwise (if the message was truncated). If non-zero is returned, ** then it is not necessary to include the nul-terminator character ** in the output buffer. */ static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf) { /* FormatMessage returns 0 on failure. Otherwise it ** returns the number of TCHARs written to the output ** buffer, excluding the terminating null char. */ DWORD error = GetLastError(); WCHAR *zTempWide = 0; DWORD dwLen; char *zOut = 0; SXUNUSED(pVfs); dwLen = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, (LPWSTR) &zTempWide, 0, 0 ); if( dwLen > 0 ){ /* allocate a buffer and convert to UTF8 */ zOut = unicodeToUtf8(zTempWide); /* free the system buffer allocated by FormatMessage */ LocalFree(zTempWide); } if( 0 == dwLen ){ Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1); }else{ /* copy a maximum of nBuf chars to output buffer */ Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */); /* free the UTF8 buffer */ HeapFree(GetProcessHeap(),0,zOut); } return 0; } /* ** Open a file. */ static int winOpen( unqlite_vfs *pVfs, /* Not used */ const char *zName, /* Name of the file (UTF-8) */ unqlite_file *id, /* Write the UnQLite file handle here */ unsigned int flags /* Open mode flags */ ){ HANDLE h; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & UNQLITE_OPEN_TEMP_DB); int isCreate = (flags & UNQLITE_OPEN_CREATE); int isReadWrite = (flags & UNQLITE_OPEN_READWRITE); pFile->h = INVALID_HANDLE_VALUE; /* Convert the filename to the system encoding. */ zConverted = convertUtf8Filename(zUtf8Name); if( zConverted==0 ){ return UNQLITE_NOMEM; } if( isReadWrite ){ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; }else{ dwDesiredAccess = GENERIC_READ; } /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is ** created. */ if( isExclusive ){ /* Creates a new file, only if it does not already exist. */ /* If the file exists, it fails. */ dwCreationDisposition = CREATE_NEW; }else if( isCreate ){ /* Open existing file, or create if it doesn't exist */ dwCreationDisposition = OPEN_ALWAYS; }else{ /* Opens a file, only if it exists. */ dwCreationDisposition = OPEN_EXISTING; } dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if( isDelete ){ dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE; }else{ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; } h = CreateFileW((WCHAR*)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); HeapFree(GetProcessHeap(),0,zConverted); return UNQLITE_IOERR; } SyZero(pFile,sizeof(*pFile)); pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->pVfs = pVfs; pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); HeapFree(GetProcessHeap(),0,zConverted); return UNQLITE_OK; } /* * Export the Windows Vfs. */ UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) { static const unqlite_vfs sWinvfs = { "Windows", /* Vfs name */ 1, /* Vfs structure version */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathName */ winOpen, /* xOpen */ winDelete, /* xDelete */ winAccess, /* xAccess */ winFullPathname, /* xFullPathname */ 0, /* xTmp */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ }; return &sWinvfs; } #endif /* __WINNT__ */