From 6c81103580d306e6d924b81bd4475ae825cd97c6 Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Sun, 11 Oct 2026 22:06:43 -0400 Subject: [PATCH 1/7] Restore legacy Windows support (XP, Win9x, WinCE) in the VFS layer Revert the 3.54.0 removal of legacy Windows support so that the engine again builds and runs on Windows XP, Windows 9x/ME and Windows CE. * src/os_win.h: restore SQLITE_OS_WINNT, SQLITE_OS_WINCE and SQLITE_WIN32_VOLATILE definitions that os_win.c and mutex_w32.c depend on. * src/os_win.c: restore the 87-entry aSyscall[] thunk table (up from the 58 entries left after the removal), including the ANSI Win32 API variants, WinCE paths, Cygwin helpers and the UUID/worker-thread entries. Restore the osIsNT() NT-vs-9x dispatch and the ANSI branches of the file, directory, link and environment operations. Restore winGetFileSizeEx()/winSetFilePointerEx() wrappers that map the Ex APIs onto GetFileSize()/SetFilePointer() for platforms that lack them, and the non-UWP Heap*/CreateFileMapping* entries. * src/mutex_w32.c: replace SRWLOCK with CRITICAL_SECTION so that the mutex implementation works on pre-Vista Windows. Keep improvements that landed after the removal, including SQLITE_ENABLE_SETLK_TIMEOUT handling, the UWP app-container paths, SQLITE_WIN32_USE_UUID, the GetTickCount64 entropy source and the SQLITE_WIN32_GETVERSIONEX / NTDDI_VERSION feature detection. A new SQLITE_WIN32_HAS_GETTICKCOUNT64 macro selects GetTickCount64 on Vista and later and falls back to GetTickCount() on XP and 9x. tool/renumber-win-thunk.tcl reports the table as consistent. --- src/mutex_w32.c | 121 +-- src/os_win.c | 1992 +++++++++++++++++++++++++++++++++++++++-------- src/os_win.h | 44 +- 3 files changed, 1784 insertions(+), 373 deletions(-) diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 0a62808953..a3bf3537de 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -53,10 +53,7 @@ ** that 128-byte alignment works better. */ struct sqlite3_mutex { - union { - CRITICAL_SECTION cs; /* The CRITICAL_SECTION mutex. id==1 */ - SRWLOCK srwl; /* The Slim reader/writer lock. id!=1 */ - } u; + CRITICAL_SECTION mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ #ifdef SQLITE_DEBUG volatile int nRef; /* Number of entrances */ @@ -68,6 +65,20 @@ struct sqlite3_mutex { #pragma warning(pop) #endif /* _MSC_VER */ +/* +** These are the initializer values used when declaring a "static" mutex +** on Win32. It should be noted that all mutexes require initialization +** on the Win32 platform. +*/ +#define SQLITE_W32_MUTEX_INITIALIZER { 0 } + +#ifdef SQLITE_DEBUG +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \ + 0L, (DWORD)0, 0 } +#else +#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id } +#endif + #ifdef SQLITE_DEBUG /* ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are @@ -77,8 +88,13 @@ static int winMutexHeld(sqlite3_mutex *p){ return p->nRef!=0 && p->owner==GetCurrentThreadId(); } +static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){ + return p->nRef==0 || p->owner!=tid; +} + static int winMutexNotheld(sqlite3_mutex *p){ - return p->nRef==0 || p->owner!=GetCurrentThreadId(); + DWORD tid = GetCurrentThreadId(); + return winMutexNotheld2(p, tid); } #endif @@ -114,13 +130,15 @@ static ALIGN128 union staticMutexes { char spacer[128]; } aWindowsMutex[12]; static int winMutex_isInit = 0; +static int winMutex_isNt = -1; /* <0 means "need to query" */ /* As the winMutexInit() and winMutexEnd() functions are called as part ** of the sqlite3_initialize() and sqlite3_shutdown() processing, the ** "interlocked" magic used here is probably not strictly necessary. */ -static LONG volatile winMutex_lock = 0; +static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; +int sqlite3_win32_is_nt(void); /* os_win.c */ void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ static int winMutexInit(void){ @@ -130,7 +148,7 @@ static int winMutexInit(void){ for(i=0; iid = i+2; - InitializeSRWLock(&p->u.srwl); + InitializeCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG p->nRef = 0; p->owner = 0; @@ -153,6 +171,11 @@ static int winMutexEnd(void){ ** (which should be the last to shutdown.) */ if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){ if( winMutex_isInit==1 ){ + int i; + for(i=0; imutex); + } winMutex_isInit = 0; } } @@ -221,11 +244,7 @@ static sqlite3_mutex *winMutexAlloc(int iType){ p->trace = 1; #endif #endif - if( iType==SQLITE_MUTEX_RECURSIVE ){ - InitializeCriticalSection(&p->u.cs); - }else{ - InitializeSRWLock(&p->u.srwl); - } + InitializeCriticalSection(&p->mutex); } break; } @@ -258,10 +277,8 @@ static sqlite3_mutex *winMutexAlloc(int iType){ static void winMutexFree(sqlite3_mutex *p){ assert( p ); assert( p->nRef==0 && p->owner==0 ); - if( p->id==SQLITE_MUTEX_FAST ){ - sqlite3_free(p); - }else if( p->id==SQLITE_MUTEX_RECURSIVE ){ - DeleteCriticalSection(&p->u.cs); + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ + DeleteCriticalSection(&p->mutex); sqlite3_free(p); }else{ #ifdef SQLITE_ENABLE_API_ARMOR @@ -282,56 +299,67 @@ static void winMutexFree(sqlite3_mutex *p){ ** more than once, the behavior is undefined. */ static void winMutexEnter(sqlite3_mutex *p){ - assert( p ); +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif #ifdef SQLITE_DEBUG - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) ); + assert( p ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); +#else + assert( p ); #endif assert( winMutex_isInit==1 ); - if( p->id==SQLITE_MUTEX_RECURSIVE ){ - EnterCriticalSection(&p->u.cs); - }else{ - AcquireSRWLockExclusive(&p->u.srwl); - } + EnterCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG assert( p->nRef>0 || p->owner==0 ); - p->owner = GetCurrentThreadId(); + p->owner = tid; p->nRef++; if( p->trace ){ OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - GetCurrentThreadId(), p->id, p, p->trace, p->nRef)); + tid, p->id, p, p->trace, p->nRef)); } #endif } static int winMutexTry(sqlite3_mutex *p){ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif int rc = SQLITE_BUSY; assert( p ); - assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); /* - ** The sqlite3_mutex_try() routine is seldom used, and when it is - ** used it is merely an optimization. So it is OK for it to always + ** The sqlite3_mutex_try() routine is very rarely used, and when it + ** is used it is merely an optimization. So it is OK for it to always ** fail. + ** + ** The TryEnterCriticalSection() interface is only available on WinNT. + ** And some windows compilers complain if you try to use it without + ** first doing some #defines that prevent SQLite from building on Win98. + ** For that reason, we will omit this optimization for now. See + ** ticket #2685. */ - if( p->id==SQLITE_MUTEX_RECURSIVE ){ - rc = TryEnterCriticalSection(&p->u.cs); - }else{ - rc = TryAcquireSRWLockExclusive(&p->u.srwl); +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400 + assert( winMutex_isInit==1 ); + assert( winMutex_isNt>=-1 && winMutex_isNt<=1 ); + if( winMutex_isNt<0 ){ + winMutex_isNt = sqlite3_win32_is_nt(); } - if( rc ){ + assert( winMutex_isNt==0 || winMutex_isNt==1 ); + if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){ #ifdef SQLITE_DEBUG - p->owner = GetCurrentThreadId(); + p->owner = tid; p->nRef++; #endif rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; } +#else + UNUSED_PARAMETER(p); +#endif #ifdef SQLITE_DEBUG if( p->trace ){ - OSTRACE(( - "TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", - GetCurrentThreadId(), p->id, p, p->trace, p->owner, p->nRef, - sqlite3ErrName(rc))); + OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n", + tid, p->id, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc))); } #endif return rc; @@ -344,24 +372,23 @@ static int winMutexTry(sqlite3_mutex *p){ ** is not currently allocated. SQLite will never do either. */ static void winMutexLeave(sqlite3_mutex *p){ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) + DWORD tid = GetCurrentThreadId(); +#endif assert( p ); #ifdef SQLITE_DEBUG assert( p->nRef>0 ); - assert( p->owner==GetCurrentThreadId() ); + assert( p->owner==tid ); p->nRef--; if( p->nRef==0 ) p->owner = 0; assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); #endif assert( winMutex_isInit==1 ); - if( p->id==SQLITE_MUTEX_RECURSIVE ){ - LeaveCriticalSection(&p->u.cs); - }else{ - ReleaseSRWLockExclusive(&p->u.srwl); - } + LeaveCriticalSection(&p->mutex); #ifdef SQLITE_DEBUG if( p->trace ){ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n", - GetCurrentThreadId(), p->id, p, p->trace, p->nRef)); + tid, p->id, p, p->trace, p->nRef)); } #endif } diff --git a/src/os_win.c b/src/os_win.c index 8c6b258094..8da7243969 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -25,6 +25,104 @@ */ #include "os_win.h" +/* +** Compiling and using WAL mode requires several APIs that are only +** available in Windows platforms based on the NT kernel. +*/ +#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) +# error "WAL mode requires support from the Windows NT kernel, compile\ + with SQLITE_OMIT_WAL." +#endif + +#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0 +# error "Memory mapped files require support from the Windows NT kernel,\ + compile with SQLITE_MAX_MMAP_SIZE=0." +#endif + +/* +** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_ANSI) +# define SQLITE_WIN32_HAS_ANSI +#endif + +/* +** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions +** based on the sub-platform)? +*/ +#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT) && \ + !defined(SQLITE_WIN32_NO_WIDE) +# define SQLITE_WIN32_HAS_WIDE +#endif + +/* +** Make sure at least one set of Win32 APIs is available. +*/ +#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE) +# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\ + must be defined." +#endif + +/* +** Define the required Windows SDK version constants if they are not +** already available. +*/ +#ifndef NTDDI_WIN8 +# define NTDDI_WIN8 0x06020000 +#endif + +#ifndef NTDDI_WINBLUE +# define NTDDI_WINBLUE 0x06030000 +#endif + +#ifndef NTDDI_WINTHRESHOLD +# define NTDDI_WINTHRESHOLD 0x06040000 +#endif + +/* +** Check to see if the GetVersionEx[AW] functions are deprecated on the +** target system. GetVersionEx was first deprecated in Win8.1. +*/ +#ifndef SQLITE_WIN32_GETVERSIONEX +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE +# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */ +# else +# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */ +# endif +#endif + +/* +** Check to see if the GetTickCount64 function is available. It first +** appeared in Windows Vista, so it is unavailable when targeting earlier +** systems such as Windows XP or Windows 9x. When no _WIN32_WINNT is +** specified, assume the most recent version of the SDK is in use. +*/ +#ifndef SQLITE_WIN32_HAS_GETTICKCOUNT64 +# if defined(SQLITE_UWP) +# define SQLITE_WIN32_HAS_GETTICKCOUNT64 1 +# elif defined(_WIN32_WCE) +# define SQLITE_WIN32_HAS_GETTICKCOUNT64 0 +# elif !defined(_WIN32_WINNT) || _WIN32_WINNT>=0x0600 +# define SQLITE_WIN32_HAS_GETTICKCOUNT64 1 +# else +# define SQLITE_WIN32_HAS_GETTICKCOUNT64 0 +# endif +#endif + +/* +** Check to see if the CreateFileMappingA function is supported on the +** target system. It is unavailable when using "mincore.lib" on Win10. +** When compiling for Windows 10, always assume "mincore.lib" is in use. +*/ +#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD +# define SQLITE_WIN32_CREATEFILEMAPPINGA 0 +# else +# define SQLITE_WIN32_CREATEFILEMAPPINGA 1 +# endif +#endif + /* ** This constant should already be defined (in the "WinDef.h" SDK file). */ @@ -104,7 +202,35 @@ #endif /* -** Some older Microsoft compilers lack the following definition. +** Do we need to manually define the Win32 file mapping APIs for use with WAL +** mode or memory mapped files (e.g. these APIs are available in the Windows +** CE SDK; however, they are not present in the header file)? +*/ +#if SQLITE_WIN32_FILEMAPPING_API && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) + +#if defined(SQLITE_WIN32_HAS_ANSI) +WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCSTR); +#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ + +#if defined(SQLITE_WIN32_HAS_WIDE) +WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ + DWORD, DWORD, DWORD, LPCWSTR); +#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ + +WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); + +/* +** These file mapping APIs are common to both Win32 and WinRT. +*/ + +WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); +WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); +#endif /* SQLITE_WIN32_FILEMAPPING_API */ + +/* +** Some Microsoft compilers lack this definition. */ #ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) @@ -124,6 +250,19 @@ typedef struct winShm winShm; /* A connection to shared-memory */ typedef struct winShmNode winShmNode; /* A region of shared-memory */ #endif +/* +** WinCE lacks native support for file locking so we have to fake it +** with some code of our own. +*/ +#if SQLITE_OS_WINCE +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; +#endif + /* ** The winFile structure is a subclass of sqlite3_file* specific to the win32 ** portability layer. @@ -142,6 +281,13 @@ struct winFile { #endif const char *zPath; /* Full pathname of this file */ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ +#if SQLITE_OS_WINCE + LPWSTR 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 #if SQLITE_MAX_MMAP_SIZE>0 int nFetchOut; /* Number of outstanding xFetch references */ HANDLE hMap; /* Handle for accessing memory mapping */ @@ -338,10 +484,46 @@ static void winMemShutdown(void *pAppData); const sqlite3_mem_methods *sqlite3MemGetWin32(void); #endif /* SQLITE_WIN32_MALLOC */ +/* +** The following variable is (normally) set once and never changes +** thereafter. It records whether the operating system is Win9x +** or WinNT. +** +** 0: Operating system unknown. +** 1: Operating system is Win9x. +** 2: Operating system is WinNT. +** +** In order to facilitate testing on a WinNT system, the test fixture +** can manually set this value to 1 to emulate Win98 behavior. +*/ +#ifdef SQLITE_TEST +LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; +#else +static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; +#endif + #ifndef SYSCALL # define SYSCALL sqlite3_syscall_ptr #endif +/* +** This function is not available on Windows CE or WinRT. + */ + +#if SQLITE_OS_WINCE +# define osAreFileApisANSI() 1 +#endif + +#ifndef SQLITE_UWP +static BOOL winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize); +static BOOL winSetFilePointerEx( + HANDLE h, /* File handle */ + LARGE_INTEGER liDistanceToMove, /* Number of bytes to move */ + PLARGE_INTEGER pNewFilePointer, /* New file pointer - may be NULL */ + DWORD dwMoveMethod /* Move method */ +); +#endif /* !SQLITE_UWP */ + /* ** Many system calls are accessed through pointer-to-functions so that ** they may be overridden at runtime to facilitate fault injection during @@ -353,43 +535,149 @@ static struct win_syscall { sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { +#if !SQLITE_OS_WINCE { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, +#else + { "AreFileApisANSI", (SYSCALL)0, 0 }, +#endif + +#ifndef osAreFileApisANSI #define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) +#endif + +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharLowerW", (SYSCALL)CharLowerW, 0 }, +#else + { "CharLowerW", (SYSCALL)0, 0 }, +#endif + +#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) + +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "CharUpperW", (SYSCALL)CharUpperW, 0 }, +#else + { "CharUpperW", (SYSCALL)0, 0 }, +#endif + +#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) { "CloseHandle", (SYSCALL)CloseHandle, 0 }, -#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[1].pCurrent) +#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "CreateFileA", (SYSCALL)CreateFileA, 0 }, +#else + { "CreateFileA", (SYSCALL)0, 0 }, +#endif + +#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) +#if defined(SQLITE_WIN32_HAS_WIDE) { "CreateFileW", (SYSCALL)CreateFileW, 0 }, +#else + { "CreateFileW", (SYSCALL)0, 0 }, +#endif + #define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ - LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[2].pCurrent) + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \ + SQLITE_WIN32_CREATEFILEMAPPINGA + { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, +#else + { "CreateFileMappingA", (SYSCALL)0, 0 }, +#endif + +#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ + DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) #if defined(SQLITE_UWP) { "CreateFileMappingFromApp",(SYSCALL)CreateFileMappingFromApp,0 }, #else { "CreateFileMappingFromApp",(SYSCALL)0, 0 }, #endif -#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE,\ - PSECURITY_ATTRIBUTES, \ - ULONG,ULONG64,PCWSTR))aSyscall[3].pCurrent) -#if !defined(SQLITE_UWP) +#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ + PSECURITY_ATTRIBUTES, ULONG,ULONG64,PCWSTR))aSyscall[7].pCurrent) + +#if (SQLITE_OS_WINCE || defined(SQLITE_WIN32_HAS_WIDE)) && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \ + !defined(SQLITE_UWP) { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, #else { "CreateFileMappingW", (SYSCALL)0, 0 }, #endif + #define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ - DWORD,DWORD,DWORD,LPCWSTR))aSyscall[4].pCurrent) + DWORD,DWORD,DWORD,LPCWSTR))aSyscall[8].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, +#else + { "CreateMutexW", (SYSCALL)0, 0 }, +#endif + +#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ + LPCWSTR))aSyscall[9].pCurrent) +#if defined(SQLITE_WIN32_HAS_ANSI) + { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, +#else + { "DeleteFileA", (SYSCALL)0, 0 }, +#endif + +#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[10].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) { "DeleteFileW", (SYSCALL)DeleteFileW, 0 }, -#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[5].pCurrent) +#else + { "DeleteFileW", (SYSCALL)0, 0 }, +#endif + +#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[11].pCurrent) + +#if SQLITE_OS_WINCE + { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 }, +#else + { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, +#endif + +#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \ + LPFILETIME))aSyscall[12].pCurrent) + +#if SQLITE_OS_WINCE + { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, +#else + { "FileTimeToSystemTime", (SYSCALL)0, 0 }, +#endif + +#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \ + LPSYSTEMTIME))aSyscall[13].pCurrent) { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, -#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[6].pCurrent) +#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[14].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, +#else + { "FormatMessageA", (SYSCALL)0, 0 }, +#endif + +#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ + DWORD,va_list*))aSyscall[15].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, +#else + { "FormatMessageW", (SYSCALL)0, 0 }, +#endif + #define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ - DWORD,va_list*))aSyscall[7].pCurrent) + DWORD,va_list*))aSyscall[16].pCurrent) #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_UWP) { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, @@ -397,53 +685,177 @@ static struct win_syscall { { "FreeLibrary", (SYSCALL)0, 0 }, #endif -#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[8].pCurrent) +#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[17].pCurrent) { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, -#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[9].pCurrent) +#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[18].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, +#else + { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, +#endif + +#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[19].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) + { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, +#else + { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, +#endif + +#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ + LPDWORD))aSyscall[20].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, +#else + { "GetFileAttributesA", (SYSCALL)0, 0 }, +#endif + +#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[21].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, -#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent) +#else + { "GetFileAttributesW", (SYSCALL)0, 0 }, +#endif + +#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[22].pCurrent) +#if defined(SQLITE_WIN32_HAS_WIDE) { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, +#else + { "GetFileAttributesExW", (SYSCALL)0, 0 }, +#endif + #define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ - LPVOID))aSyscall[11].pCurrent) + LPVOID))aSyscall[23].pCurrent) + +#ifndef SQLITE_UWP + { "GetFileSize", (SYSCALL)GetFileSize, 0 }, +#else + { "GetFileSize", (SYSCALL)0, 0 }, +#endif + +#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[24].pCurrent) +#ifdef SQLITE_UWP { "GetFileSizeEx", (SYSCALL)GetFileSizeEx, 0 }, +#else + { "GetFileSizeEx", (SYSCALL)winGetFileSizeEx, 0 }, +#endif + #define osGetFileSizeEx ((BOOL(WINAPI*)(HANDLE, \ - PLARGE_INTEGER))aSyscall[12].pCurrent) + PLARGE_INTEGER))aSyscall[25].pCurrent) +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) + { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, +#else + { "GetFullPathNameA", (SYSCALL)0, 0 }, +#endif + +#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ + LPSTR*))aSyscall[26].pCurrent) + +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, +#else + { "GetFullPathNameW", (SYSCALL)0, 0 }, +#endif + #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ - LPWSTR*))aSyscall[13].pCurrent) + LPWSTR*))aSyscall[27].pCurrent) +/* +** For GetLastError(), MSDN says: +** +** Minimum supported client: Windows XP [desktop apps | UWP apps] +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] +*/ { "GetLastError", (SYSCALL)GetLastError, 0 }, -#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[14].pCurrent) + +#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[28].pCurrent) #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_UWP) +#if SQLITE_OS_WINCE + /* The GetProcAddressA() routine is only available on Windows CE. */ + { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, +#else + /* All other Windows platforms expect GetProcAddress() to take + ** an ANSI string regardless of the _UNICODE setting */ { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, +#endif #else { "GetProcAddressA", (SYSCALL)0, 0 }, #endif + #define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ - LPCSTR))aSyscall[15].pCurrent) + LPCSTR))aSyscall[29].pCurrent) { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, -#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[16].pCurrent) +#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[30].pCurrent) + + { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, +#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[31].pCurrent) +#if !SQLITE_OS_WINCE { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, +#else + { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, +#endif + #define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ - LPFILETIME))aSyscall[17].pCurrent) + LPFILETIME))aSyscall[32].pCurrent) -#ifdef SQLITE_UWP - { "GetTempPathW", (SYSCALL)0, 0 }, +#if defined(SQLITE_WIN32_HAS_ANSI) + { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, #else + { "GetTempPathA", (SYSCALL)0, 0 }, +#endif + +#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[33].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) && !defined(SQLITE_UWP) { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, +#else + { "GetTempPathW", (SYSCALL)0, 0 }, #endif -#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[18].pCurrent) +#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[34].pCurrent) + + { "GetTickCount", (SYSCALL)GetTickCount, 0 }, + +#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[35].pCurrent) + +#if SQLITE_WIN32_HAS_GETTICKCOUNT64 { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[19].pCurrent) +#else + { "GetTickCount64", (SYSCALL)0, 0 }, +#endif + +#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[36].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX + { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, +#else + { "GetVersionExA", (SYSCALL)0, 0 }, +#endif + +#define osGetVersionExA ((BOOL(WINAPI*)( \ + LPOSVERSIONINFOA))aSyscall[37].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) && \ + SQLITE_WIN32_GETVERSIONEX + { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, +#else + { "GetVersionExW", (SYSCALL)0, 0 }, +#endif + +#define osGetVersionExW ((BOOL(WINAPI*)( \ + LPOSVERSIONINFOW))aSyscall[38].pCurrent) #ifdef SQLITE_UWP { "HeapAlloc", (SYSCALL)0, 0 }, @@ -451,7 +863,7 @@ static struct win_syscall { { "HeapAlloc", (SYSCALL)HeapAlloc, 0 }, #endif #define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ - SIZE_T))aSyscall[20].pCurrent) + SIZE_T))aSyscall[39].pCurrent) #ifdef SQLITE_UWP { "HeapCreate", (SYSCALL)0, 0 }, @@ -459,21 +871,21 @@ static struct win_syscall { { "HeapCreate", (SYSCALL)HeapCreate, 0 }, #endif #define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ - SIZE_T))aSyscall[21].pCurrent) + SIZE_T))aSyscall[40].pCurrent) #ifdef SQLITE_UWP { "HeapDestroy", (SYSCALL)0, 0 }, #else { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, #endif -#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[22].pCurrent) +#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[41].pCurrent) #ifdef SQLITE_UWP { "HeapFree", (SYSCALL)0, 0 }, #else { "HeapFree", (SYSCALL)HeapFree, 0 }, #endif -#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[23].pCurrent) +#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[42].pCurrent) #ifdef SQLITE_UWP { "HeapReAlloc", (SYSCALL)0, 0 }, @@ -481,7 +893,7 @@ static struct win_syscall { { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, #endif #define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ - SIZE_T))aSyscall[24].pCurrent) + SIZE_T))aSyscall[43].pCurrent) #ifdef SQLITE_UWP { "HeapSize", (SYSCALL)0, 0 }, @@ -489,7 +901,7 @@ static struct win_syscall { { "HeapSize", (SYSCALL)HeapSize, 0 }, #endif #define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[25].pCurrent) + LPCVOID))aSyscall[44].pCurrent) #ifdef SQLITE_UWP { "HeapValidate", (SYSCALL)0, 0 }, @@ -497,38 +909,69 @@ static struct win_syscall { { "HeapValidate", (SYSCALL)HeapValidate, 0 }, #endif #define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ - LPCVOID))aSyscall[26].pCurrent) + LPCVOID))aSyscall[45].pCurrent) -#ifdef SQLITE_UWP +#if !SQLITE_OS_WINCE && !defined(SQLITE_UWP) + { "HeapCompact", (SYSCALL)HeapCompact, 0 }, +#else { "HeapCompact", (SYSCALL)0, 0 }, +#endif + +#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[46].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) \ + && !defined(SQLITE_UWP) + { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, #else - { "HeapCompact", (SYSCALL)HeapCompact, 0 }, + { "LoadLibraryA", (SYSCALL)0, 0 }, #endif -#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[27].pCurrent) +#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[47].pCurrent) -#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_UWP) +#if defined(SQLITE_WIN32_HAS_WIDE) && \ + !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_UWP) { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, #else { "LoadLibraryW", (SYSCALL)0, 0 }, #endif -#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[28].pCurrent) + +#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[48].pCurrent) { "LocalFree", (SYSCALL)LocalFree, 0 }, -#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[29].pCurrent) +#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[49].pCurrent) + +#if !SQLITE_OS_WINCE + { "LockFile", (SYSCALL)LockFile, 0 }, +#else + { "LockFile", (SYSCALL)0, 0 }, +#endif + +#if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI) +#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[50].pCurrent) +#endif + +#if !SQLITE_OS_WINCE { "LockFileEx", (SYSCALL)LockFileEx, 0 }, +#else + { "LockFileEx", (SYSCALL)0, 0 }, +#endif + +#ifndef osLockFileEx #define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[30].pCurrent) + LPOVERLAPPED))aSyscall[51].pCurrent) +#endif -#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ - && !defined(SQLITE_UWP) +#if (SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || \ + SQLITE_MAX_MMAP_SIZE>0) && !defined(SQLITE_UWP) { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, #else { "MapViewOfFile", (SYSCALL)0, 0 }, #endif + #define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - SIZE_T))aSyscall[31].pCurrent) + SIZE_T))aSyscall[52].pCurrent) #if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ && defined(SQLITE_UWP) @@ -537,62 +980,136 @@ static struct win_syscall { { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, #endif #define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ - SIZE_T))aSyscall[32].pCurrent) + SIZE_T))aSyscall[53].pCurrent) { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, + #define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ - int))aSyscall[33].pCurrent) + int))aSyscall[54].pCurrent) { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, + #define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ - LARGE_INTEGER*))aSyscall[34].pCurrent) + LARGE_INTEGER*))aSyscall[55].pCurrent) { "ReadFile", (SYSCALL)ReadFile, 0 }, + #define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[35].pCurrent) + LPOVERLAPPED))aSyscall[56].pCurrent) { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, -#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[36].pCurrent) +#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[57].pCurrent) + +#ifndef SQLITE_UWP + { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, +#else + { "SetFilePointer", (SYSCALL)0, 0 }, +#endif + +#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ + DWORD))aSyscall[58].pCurrent) + +#ifdef SQLITE_UWP { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, -#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER,\ - PLARGE_INTEGER,DWORD))aSyscall[37].pCurrent) +#else + { "SetFilePointerEx", (SYSCALL)winSetFilePointerEx, 0 }, +#endif + +#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ + PLARGE_INTEGER,DWORD))aSyscall[59].pCurrent) { "Sleep", (SYSCALL)Sleep, 0 }, -#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[38].pCurrent) +#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[60].pCurrent) + + { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, + +#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \ + LPFILETIME))aSyscall[61].pCurrent) + +#if !SQLITE_OS_WINCE + { "UnlockFile", (SYSCALL)UnlockFile, 0 }, +#else + { "UnlockFile", (SYSCALL)0, 0 }, +#endif + +#if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI) +#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ + DWORD))aSyscall[62].pCurrent) +#endif + +#if !SQLITE_OS_WINCE { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, +#else + { "UnlockFileEx", (SYSCALL)0, 0 }, +#endif + #define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ - LPOVERLAPPED))aSyscall[39].pCurrent) + LPOVERLAPPED))aSyscall[63].pCurrent) -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, #else { "UnmapViewOfFile", (SYSCALL)0, 0 }, #endif -#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[40].pCurrent) + +#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[64].pCurrent) { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, + #define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ - LPCSTR,LPBOOL))aSyscall[41].pCurrent) + LPCSTR,LPBOOL))aSyscall[65].pCurrent) { "WriteFile", (SYSCALL)WriteFile, 0 }, + #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ - LPOVERLAPPED))aSyscall[42].pCurrent) + LPOVERLAPPED))aSyscall[66].pCurrent) +/* +** For WaitForSingleObject(), MSDN says: +** +** Minimum supported client: Windows XP [desktop apps | UWP apps] +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] +*/ { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, + #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[43].pCurrent) + DWORD))aSyscall[67].pCurrent) +#if !SQLITE_OS_WINCE { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, +#else + { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, +#endif + #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ - BOOL))aSyscall[44].pCurrent) + BOOL))aSyscall[68].pCurrent) + + { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, +#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ + LPSYSTEM_INFO))aSyscall[69].pCurrent) + +#if defined(SQLITE_WIN32_HAS_ANSI) { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[45].pCurrent) +#else + { "OutputDebugStringA", (SYSCALL)0, 0 }, +#endif + +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[70].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) + { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, +#else + { "OutputDebugStringW", (SYSCALL)0, 0 }, +#endif + +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[71].pCurrent) { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[46].pCurrent) + +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[72].pCurrent) /* ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" @@ -601,99 +1118,172 @@ static struct win_syscall { */ #if defined(InterlockedCompareExchange) { "InterlockedCompareExchange", (SYSCALL)0, 0 }, + #define osInterlockedCompareExchange InterlockedCompareExchange #else { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, -#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG volatile*,\ - LONG,LONG))aSyscall[47].pCurrent) + +#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ + SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[73].pCurrent) #endif /* defined(InterlockedCompareExchange) */ -#if SQLITE_WIN32_USE_UUID +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID { "UuidCreate", (SYSCALL)UuidCreate, 0 }, #else { "UuidCreate", (SYSCALL)0, 0 }, #endif -#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[48].pCurrent) -#if SQLITE_WIN32_USE_UUID +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[74].pCurrent) + +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, #else { "UuidCreateSequential", (SYSCALL)0, 0 }, #endif + #define osUuidCreateSequential \ - ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[49].pCurrent) + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[75].pCurrent) #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, #else { "FlushViewOfFile", (SYSCALL)0, 0 }, #endif + #define osFlushViewOfFile \ - ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[50].pCurrent) + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[76].pCurrent) +/* +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() +** to implement blocking locks with timeouts. MSDN says: +** +** Minimum supported client: Windows XP [desktop apps | UWP apps] +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] +*/ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT { "CreateEvent", (SYSCALL)CreateEvent, 0 }, #else { "CreateEvent", (SYSCALL)0, 0 }, #endif -#define osCreateEvent ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ - BOOL,LPCSTR))aSyscall[51].pCurrent) +#define osCreateEvent ( \ + (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ + aSyscall[77].pCurrent \ +) + +/* +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() +** for the case where a timeout expires and a lock request must be +** cancelled. +** +** Minimum supported client: Windows XP [desktop apps | UWP apps] +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] +*/ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT { "CancelIo", (SYSCALL)CancelIo, 0 }, #else { "CancelIo", (SYSCALL)0, 0 }, #endif -#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[52].pCurrent) + +#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[78].pCurrent) + +#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) + { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, +#else + { "GetModuleHandleW", (SYSCALL)0, 0 }, +#endif + +#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[79].pCurrent) #ifndef _WIN32 { "getenv", (SYSCALL)getenv, 0 }, #else { "getenv", (SYSCALL)0, 0 }, #endif -#define osGetenv ((const char *(*)(const char *))aSyscall[53].pCurrent) + +#define osGetenv ((const char *(*)(const char *))aSyscall[80].pCurrent) #ifndef _WIN32 { "getcwd", (SYSCALL)getcwd, 0 }, #else { "getcwd", (SYSCALL)0, 0 }, #endif -#define osGetcwd ((char*(*)(char*,size_t))aSyscall[54].pCurrent) + +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[81].pCurrent) #ifndef _WIN32 { "readlink", (SYSCALL)readlink, 0 }, #else { "readlink", (SYSCALL)0, 0 }, #endif -#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[55].pCurrent) + +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[82].pCurrent) #ifndef _WIN32 { "lstat", (SYSCALL)lstat, 0 }, #else { "lstat", (SYSCALL)0, 0 }, #endif -#define osLstat ((int(*)(const char*,struct stat*))aSyscall[56].pCurrent) + +#define osLstat ((int(*)(const char*,struct stat*))aSyscall[83].pCurrent) #ifndef _WIN32 { "__errno", (SYSCALL)__errno, 0 }, #else { "__errno", (SYSCALL)0, 0 }, #endif -#define osErrno (*((int*(*)(void))aSyscall[57].pCurrent)()) + +#define osErrno (*((int*(*)(void))aSyscall[84].pCurrent)()) #ifndef _WIN32 { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, #else { "cygwin_conv_path", (SYSCALL)0, 0 }, #endif + #define osCygwin_conv_path ((size_t(*)(unsigned int, \ - const void *, void *, size_t))aSyscall[58].pCurrent) + const void *, void *, size_t))aSyscall[85].pCurrent) { "GetDriveTypeW", (SYSCALL)GetDriveTypeW, 0 }, -#define osGetDriveTypeW ((UINT(WINAPI*)(LPCWSTR))aSyscall[59].pCurrent) +#define osGetDriveTypeW ((UINT(WINAPI*)(LPCWSTR))aSyscall[86].pCurrent) }; /* End of the overrideable system calls */ +#ifndef SQLITE_UWP +/* +** The GetFileSizeEx() and SetFilePointerEx() interfaces are not available +** on older versions of Windows (Windows 95, 98, ME, NT4, and 2000) that +** SQLite still supports, so provide equivalent implementations in terms of +** the older GetFileSize() and SetFilePointer() interfaces. Both routines +** return non-zero upon success and zero upon failure. +*/ +static BOOL winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize){ + DWORD szHigh = 0; + DWORD szLow = osGetFileSize(h, &szHigh); + if( szLow==0xFFFFFFFF && osGetLastError()!=NO_ERROR ) return 0; + pFileSize->LowPart = szLow; + pFileSize->HighPart = (LONG)szHigh; + return 1; +} + +static BOOL winSetFilePointerEx( + HANDLE h, /* File handle */ + LARGE_INTEGER liDistanceToMove, /* Number of bytes to move */ + PLARGE_INTEGER pNewFilePointer, /* New file pointer - may be NULL */ + DWORD dwMoveMethod /* Move method */ +){ + LONG dwHigh = liDistanceToMove.HighPart; + DWORD dwRet = osSetFilePointer(h, liDistanceToMove.LowPart, &dwHigh, + dwMoveMethod); + if( dwRet==0xFFFFFFFF && osGetLastError()!=NO_ERROR ) return 0; + if( pNewFilePointer ){ + pNewFilePointer->LowPart = dwRet; + pNewFilePointer->HighPart = dwHigh; + } + return 1; +} +#endif /* !SQLITE_UWP */ + /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the ** "win32" VFSes. Return SQLITE_OK upon successfully updating the @@ -800,6 +1390,7 @@ int sqlite3_win32_compact_heap(LPUINT pnLargest){ #if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif +#if !SQLITE_OS_WINCE if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ DWORD lastErrno = osGetLastError(); if( lastErrno==NO_ERROR ){ @@ -812,6 +1403,11 @@ int sqlite3_win32_compact_heap(LPUINT pnLargest){ rc = SQLITE_ERROR; } } +#else + sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p", + (void*)hHeap); + rc = SQLITE_NOTFOUND; +#endif if( pnLargest ) *pnLargest = nLargest; return rc; } @@ -865,8 +1461,9 @@ int sqlite3_win32_reset_heap(){ #ifdef _WIN32 /* ** This function outputs the specified (ANSI) string to the Win32 debugger -** (if available). Undocumented. Might go away at any moment. +** (if available). */ + void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ @@ -878,6 +1475,7 @@ void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ return; } #endif +#if defined(SQLITE_WIN32_HAS_ANSI) if( nMin>0 ){ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); memcpy(zDbgBuf, zBuf, nMin); @@ -885,13 +1483,32 @@ void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ }else{ osOutputDebugStringA(zBuf); } +#elif defined(SQLITE_WIN32_HAS_WIDE) + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); + if ( osMultiByteToWideChar( + osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf, + nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){ + return; + } + osOutputDebugStringW((LPCWSTR)zDbgBuf); +#else + if( nMin>0 ){ + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); + memcpy(zDbgBuf, zBuf, nMin); + fprintf(stderr, "%s", zDbgBuf); + }else{ + fprintf(stderr, "%s", zBuf); + } +#endif } +#endif /* _WIN32 */ void sqlite3_win32_sleep(DWORD milliseconds){ osSleep(milliseconds); } -#if SQLITE_MAX_WORKER_THREADS>0 && SQLITE_THREADSAFE>0 +#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && \ + SQLITE_THREADSAFE>0 DWORD sqlite3Win32Wait(HANDLE hObject){ DWORD rc; while( (rc = osWaitForSingleObjectEx(hObject, INFINITE, @@ -899,7 +1516,63 @@ DWORD sqlite3Win32Wait(HANDLE hObject){ return rc; } #endif -#endif /* _WIN32 */ + +/* +** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, +** or WinCE. Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation: Win95, Win98, and WinME lack +** the LockFileEx() API. But we can still statically link against that +** API as long as we don't call it when running Win95/98/ME. A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. +*/ + +#if !SQLITE_WIN32_GETVERSIONEX +# define osIsNT() (1) +#elif SQLITE_OS_WINCE || !defined(SQLITE_WIN32_HAS_ANSI) +# define osIsNT() (1) +#elif !defined(SQLITE_WIN32_HAS_WIDE) +# define osIsNT() (0) +#else +# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt()) +#endif + +/* +** This function determines if the machine is running a version of Windows +** based on the NT kernel. +*/ +int sqlite3_win32_is_nt(void){ +#if SQLITE_WIN32_GETVERSIONEX + if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){ +#if defined(SQLITE_WIN32_HAS_ANSI) + OSVERSIONINFOA sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + osGetVersionExA(&sInfo); + osInterlockedCompareExchange(&sqlite3_os_type, + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); +#elif defined(SQLITE_WIN32_HAS_WIDE) + OSVERSIONINFOW sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + osGetVersionExW(&sInfo); + osInterlockedCompareExchange(&sqlite3_os_type, + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); +#endif + } + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; +#elif SQLITE_TEST + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2 + || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 + ; +#else + /* + ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are + ** deprecated are always assumed to be based on the NT kernel. + */ + return 1; +#endif +} #ifdef SQLITE_WIN32_MALLOC /* @@ -1440,24 +2113,49 @@ static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ */ DWORD dwLen = 0; char *zOut = 0; - LPWSTR zTempWide = NULL; - dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - (LPWSTR) &zTempWide, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - sqlite3BeginBenignMalloc(); - zOut = winUnicodeToUtf8(zTempWide); - sqlite3EndBenignMalloc(); - /* free the system buffer allocated by FormatMessage */ - osLocalFree(zTempWide); + + if( osIsNT() ){ + LPWSTR zTempWide = NULL; + dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPWSTR) &zTempWide, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + sqlite3BeginBenignMalloc(); + zOut = winUnicodeToUtf8(zTempWide); + sqlite3EndBenignMalloc(); + /* free the system buffer allocated by FormatMessage */ + osLocalFree(zTempWide); + } } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + char *zTemp = NULL; + dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + lastErrno, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + sqlite3BeginBenignMalloc(); + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); + sqlite3EndBenignMalloc(); + /* free the system buffer allocated by FormatMessage */ + osLocalFree(zTemp); + } + } +#endif if( 0 == dwLen ){ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno); }else{ @@ -1593,6 +2291,323 @@ static void winLogIoerr(int nRetry, int lineno){ } } +/* +** This #if does not rely on the SQLITE_OS_WINCE define because the +** corresponding section in "date.c" cannot use it. +*/ +#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \ + (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API) +/* +** The MSVC CRT on Windows CE may not have a localtime() function. +** So define a substitute. +*/ +# include +struct tm *__cdecl localtime(const time_t *t) +{ + static struct tm y; + FILETIME uTm, lTm; + SYSTEMTIME pTm; + sqlite3_int64 t64; + t64 = *t; + t64 = (t64 + 11644473600)*10000000; + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); + uTm.dwHighDateTime= (DWORD)(t64 >> 32); + osFileTimeToLocalFileTime(&uTm,&lTm); + osFileTimeToSystemTime(&lTm,&pTm); + y.tm_year = pTm.wYear - 1900; + y.tm_mon = pTm.wMonth - 1; + y.tm_wday = pTm.wDayOfWeek; + y.tm_mday = pTm.wDay; + y.tm_hour = pTm.wHour; + y.tm_min = pTm.wMinute; + y.tm_sec = pTm.wSecond; + return &y; +} +#endif + +#if SQLITE_OS_WINCE +/************************************************************************* +** This section contains code for WinCE only. +*/ +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] + +/* +** Acquire a lock on the handle h +*/ +static void winceMutexAcquire(HANDLE h){ + DWORD dwErr; + do { + dwErr = osWaitForSingleObject(h, INFINITE); + } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); +} +/* +** Release a lock acquired by winceMutexAcquire() +*/ +#define winceMutexRelease(h) ReleaseMutex(h) + +/* +** Create the mutex and shared memory used for locking in the file +** descriptor pFile +*/ +static int winceCreateLock(const char *zFilename, winFile *pFile){ + LPWSTR zTok; + LPWSTR zName; + DWORD lastErrno; + BOOL bLogged = FALSE; + BOOL bInit = TRUE; + + zName = winUtf8ToUnicode(zFilename); + if( zName==0 ){ + /* out of memory */ + return SQLITE_IOERR_NOMEM_BKPT; + } + + /* Initialize the local lockdata */ + memset(&pFile->local, 0, sizeof(pFile->local)); + + /* Replace the backslashes from the filename and lowercase it + ** to derive a mutex name. */ + zTok = osCharLowerW(zName); + for (;*zTok;zTok++){ + if (*zTok == '\\') *zTok = '_'; + } + + /* Create/open the named mutex */ + pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); + if (!pFile->hMutex){ + pFile->lastErrno = osGetLastError(); + sqlite3_free(zName); + return winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock1", zFilename); + } + + /* Acquire the mutex before continuing */ + winceMutexAcquire(pFile->hMutex); + + /* Since the names of named mutexes, semaphores, file mappings etc are + ** case-sensitive, take advantage of that by uppercasing the mutex name + ** and using that as the shared filemapping name. + */ + osCharUpperW(zName); + pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(winceLock), + zName); + + /* Set a flag that indicates we're the first to create the memory so it + ** must be zero-initialized */ + lastErrno = osGetLastError(); + if (lastErrno == ERROR_ALREADY_EXISTS){ + bInit = FALSE; + } + + sqlite3_free(zName); + + /* If we succeeded in making the shared memory handle, map it. */ + if( pFile->hShared ){ + pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); + /* If mapping failed, close the shared memory handle and erase it */ + if( !pFile->shared ){ + pFile->lastErrno = osGetLastError(); + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock2", zFilename); + bLogged = TRUE; + osCloseHandle(pFile->hShared); + pFile->hShared = NULL; + } + } + + /* If shared memory could not be created, then close the mutex and fail */ + if( pFile->hShared==NULL ){ + if( !bLogged ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock3", zFilename); + bLogged = TRUE; + } + winceMutexRelease(pFile->hMutex); + osCloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + return SQLITE_IOERR; + } + + /* Initialize the shared memory if we're supposed to */ + if( bInit ){ + memset(pFile->shared, 0, sizeof(winceLock)); + } + + winceMutexRelease(pFile->hMutex); + return SQLITE_OK; +} + +/* +** Destroy the part of winFile that deals with wince locks +*/ +static void winceDestroyLock(winFile *pFile){ + if (pFile->hMutex){ + /* Acquire the mutex */ + winceMutexAcquire(pFile->hMutex); + + /* The following blocks should probably assert in debug mode, but they + are to cleanup in case any locks remained open */ + if (pFile->local.nReaders){ + pFile->shared->nReaders --; + } + if (pFile->local.bReserved){ + pFile->shared->bReserved = FALSE; + } + if (pFile->local.bPending){ + pFile->shared->bPending = FALSE; + } + if (pFile->local.bExclusive){ + pFile->shared->bExclusive = FALSE; + } + + /* De-reference and close our copy of the shared memory handle */ + osUnmapViewOfFile(pFile->shared); + osCloseHandle(pFile->hShared); + + /* Done with the mutex */ + winceMutexRelease(pFile->hMutex); + osCloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + } +} + +/* +** An implementation of the LockFile() API of Windows for CE +*/ +static BOOL winceLockFile( + LPHANDLE phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; + + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Wanting an exclusive lock? */ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST + && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ + if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ + pFile->shared->bExclusive = TRUE; + pFile->local.bExclusive = TRUE; + bReturn = TRUE; + } + } + + /* Want a read-only lock? */ + else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && + nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bExclusive == 0){ + pFile->local.nReaders ++; + if (pFile->local.nReaders == 1){ + pFile->shared->nReaders ++; + } + bReturn = TRUE; + } + } + + /* Want a pending lock? */ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE + && nNumberOfBytesToLockLow == 1){ + /* If no pending lock has been acquired, then acquire it */ + if (pFile->shared->bPending == 0) { + pFile->shared->bPending = TRUE; + pFile->local.bPending = TRUE; + bReturn = TRUE; + } + } + + /* Want a reserved lock? */ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bReserved == 0) { + pFile->shared->bReserved = TRUE; + pFile->local.bReserved = TRUE; + bReturn = TRUE; + } + } + + winceMutexRelease(pFile->hMutex); + return bReturn; +} + +/* +** An implementation of the UnlockFile API of Windows for CE +*/ +static BOOL winceUnlockFile( + LPHANDLE phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; + + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Releasing a reader lock or an exclusive lock */ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ + /* Did we have an exclusive lock? */ + if (pFile->local.bExclusive){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); + pFile->local.bExclusive = FALSE; + pFile->shared->bExclusive = FALSE; + bReturn = TRUE; + } + + /* Did we just have a reader lock? */ + else if (pFile->local.nReaders){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE + || nNumberOfBytesToUnlockLow == 1); + pFile->local.nReaders --; + if (pFile->local.nReaders == 0) + { + pFile->shared->nReaders --; + } + bReturn = TRUE; + } + } + + /* Releasing a pending lock */ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE + && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bPending){ + pFile->local.bPending = FALSE; + pFile->shared->bPending = FALSE; + bReturn = TRUE; + } + } + /* Releasing a reserved lock */ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bReserved) { + pFile->local.bReserved = FALSE; + pFile->shared->bReserved = FALSE; + bReturn = TRUE; + } + } + + winceMutexRelease(pFile->hMutex); + return bReturn; +} +/* +** End of the special code for wince +*****************************************************************************/ +#endif /* SQLITE_OS_WINCE */ /* ** Lock a file region. @@ -1605,11 +2620,27 @@ static BOOL winLockFile( DWORD numBytesLow, DWORD numBytesHigh ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API LockFile. + */ + return winceLockFile(phFile, offsetLow, offsetHigh, + numBytesLow, numBytesHigh); +#else + if( osIsNT() ){ + OVERLAPPED ovlp; + memset(&ovlp, 0, sizeof(OVERLAPPED)); + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); +#ifdef SQLITE_WIN32_HAS_ANSI + }else{ + return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); +#endif + } +#endif } #ifndef SQLITE_OMIT_WAL @@ -1633,56 +2664,63 @@ static int winHandleLockTimeout( DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0); int rc = SQLITE_OK; BOOL ret; - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offset; -#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) - if( nMs!=0 ){ - flags &= ~LOCKFILE_FAIL_IMMEDIATELY; - } - ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); - if( ovlp.hEvent==NULL ){ - return SQLITE_IOERR_LOCK; - } + if( !osIsNT() ){ + ret = winLockFile(&hFile, flags, offset, 0, nByte, 0); + }else{ + OVERLAPPED ovlp; + memset(&ovlp, 0, sizeof(OVERLAPPED)); + ovlp.Offset = offset; + +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( nMs!=0 ){ + flags &= ~LOCKFILE_FAIL_IMMEDIATELY; + } + ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); + if( ovlp.hEvent==NULL ){ + return SQLITE_IOERR_LOCK; + } #endif - ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); -#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) - /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was - ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to - ** LockFileEx() may fail because the request is still pending. This can - ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. - ** - ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags - ** passed to LockFileEx(). In this case, if the operation is pending, - ** block indefinitely until it is finished. - ** - ** Otherwise, wait for up to nMs ms for the operation to finish. nMs - ** may be set to INFINITE. - */ - if( !ret && GetLastError()==ERROR_IO_PENDING ){ - DWORD nDelay = (nMs==0 ? INFINITE : nMs); - DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); - if( res==WAIT_OBJECT_0 ){ - ret = TRUE; - }else if( res==WAIT_TIMEOUT ){ + + ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); + +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was + ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to + ** LockFileEx() may fail because the request is still pending. This can + ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. + ** + ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags + ** passed to LockFileEx(). In this case, if the operation is pending, + ** block indefinitely until it is finished. + ** + ** Otherwise, wait for up to nMs ms for the operation to finish. nMs + ** may be set to INFINITE. + */ + if( !ret && GetLastError()==ERROR_IO_PENDING ){ + DWORD nDelay = (nMs==0 ? INFINITE : nMs); + DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); + if( res==WAIT_OBJECT_0 ){ + ret = TRUE; + }else if( res==WAIT_TIMEOUT ){ #if SQLITE_ENABLE_SETLK_TIMEOUT==1 - rc = SQLITE_BUSY_TIMEOUT; + rc = SQLITE_BUSY_TIMEOUT; #else - rc = SQLITE_BUSY; + rc = SQLITE_BUSY; #endif - }else{ - /* Some other error has occurred */ - rc = SQLITE_IOERR_LOCK; + }else{ + /* Some other error has occurred */ + rc = SQLITE_IOERR_LOCK; + } + + /* If it is still pending, cancel the LockFileEx() call. */ + osCancelIo(hFile); } - /* If it is still pending, cancel the LockFileEx() call. */ - osCancelIo(hFile); + osCloseHandle(ovlp.hEvent); +#endif } - osCloseHandle(ovlp.hEvent); -#endif /* defined(SQLITE_ENABLE_SETLK_TIMEOUT) */ - if( rc==SQLITE_OK && !ret ){ rc = SQLITE_BUSY; } @@ -1700,11 +2738,27 @@ static BOOL winUnlockFile( DWORD numBytesLow, DWORD numBytesHigh ){ - OVERLAPPED ovlp; - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = offsetLow; - ovlp.OffsetHigh = offsetHigh; - return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API UnlockFile. + */ + return winceUnlockFile(phFile, offsetLow, offsetHigh, + numBytesLow, numBytesHigh); +#else + if( osIsNT() ){ + OVERLAPPED ovlp; + memset(&ovlp, 0, sizeof(OVERLAPPED)); + ovlp.Offset = offsetLow; + ovlp.OffsetHigh = offsetHigh; + return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); +#ifdef SQLITE_WIN32_HAS_ANSI + }else{ + return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, + numBytesHigh); +#endif + } +#endif } #ifndef SQLITE_OMIT_WAL @@ -1801,6 +2855,26 @@ static int winClose(sqlite3_file *id){ rc = osCloseHandle(pFile->h); /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (osSleep(100), 1) ); +#if SQLITE_OS_WINCE +#define WINCE_DELETION_ATTEMPTS 3 + { + winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData; + if( pAppData==NULL || !pAppData->bNoLock ){ + winceDestroyLock(pFile); + } + } + if( pFile->zDeleteOnClose ){ + int cnt = 0; + while( + osDeleteFileW(pFile->zDeleteOnClose)==0 + && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff + && cnt++ < WINCE_DELETION_ATTEMPTS + ){ + sqlite3_win32_sleep(100); /* Wait a little before trying again */ + } + sqlite3_free(pFile->zDeleteOnClose); + } +#endif if( rc ){ pFile->h = NULL; } @@ -1823,7 +2897,7 @@ static int winRead( int amt, /* Number of bytes to read */ sqlite3_int64 offset /* Begin reading at this offset */ ){ -#if !defined(SQLITE_WIN32_NO_OVERLAPPED) +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) OVERLAPPED overlapped; /* The offset for ReadFile. */ #endif winFile *pFile = (winFile*)id; /* file handle */ @@ -1857,7 +2931,7 @@ static int winRead( } #endif -#if defined(SQLITE_WIN32_NO_OVERLAPPED) +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) if( winSeekFile(pFile, offset) ){ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", osGetCurrentProcessId(), pFile, pFile->h)); @@ -1935,13 +3009,13 @@ static int winWrite( } #endif -#if defined(SQLITE_WIN32_NO_OVERLAPPED) +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) rc = winSeekFile(pFile, offset); if( rc==0 ){ #else { #endif -#if !defined(SQLITE_WIN32_NO_OVERLAPPED) +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) OVERLAPPED overlapped; /* The offset for WriteFile. */ #endif u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ @@ -1949,14 +3023,14 @@ static int winWrite( DWORD nWrite; /* Bytes written by each WriteFile() call */ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ -#if !defined(SQLITE_WIN32_NO_OVERLAPPED) +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) memset(&overlapped, 0, sizeof(OVERLAPPED)); overlapped.Offset = (LONG)(offset & 0xffffffff); overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); #endif while( nRem>0 ){ -#if defined(SQLITE_WIN32_NO_OVERLAPPED) +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ #else if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ @@ -1969,7 +3043,7 @@ static int winWrite( lastErrno = osGetLastError(); break; } -#if !defined(SQLITE_WIN32_NO_OVERLAPPED) +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) offset += nWrite; overlapped.Offset = (LONG)(offset & 0xffffffff); overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); @@ -2286,8 +3360,27 @@ static int winGetReadLock(winFile *pFile, int bBlock){ int res; DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, - SHARED_SIZE, 0); + if( osIsNT() ){ +#if SQLITE_OS_WINCE + /* + ** NOTE: Windows CE is handled differently here due its lack of the Win32 + ** API LockFileEx. + */ + res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); +#else + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, + SHARED_SIZE, 0); +#endif + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + int lk; + sqlite3_randomness(sizeof(lk), &lk); + pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, + SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + } +#endif if( res == 0 ){ pFile->lastErrno = osGetLastError(); /* No need to log a failure to lock */ @@ -2303,7 +3396,14 @@ static int winUnlockReadLock(winFile *pFile){ int res; DWORD lastErrno; OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + if( osIsNT() ){ + res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); + } +#endif if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, @@ -2855,70 +3955,80 @@ static SYSTEM_INFO winSysInfo; ** happen when the file path >32k, in which case winUtf8ToUnicode() ** will fail too. */ -static LPWSTR winConvertFromUtf8Filename(const char *zFilename){ - LPWSTR zConverted = 0; +static void *winConvertFromUtf8Filename(const char *zFilename){ + void *zConverted = 0; + if( osIsNT() ){ #ifdef __CYGWIN__ - int nChar; - - if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename) - && winIsDirSep(zFilename[2])) ){ - i64 nByte; - int convertflag = CCP_POSIX_TO_WIN_W; - if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE; - nByte = (i64)osCygwin_conv_path(convertflag, - zFilename, 0, 0); - if( nByte>0 ){ - zConverted = sqlite3MallocZero(12+(u64)nByte); - if ( zConverted==0 ){ - return zConverted; - } - /* Filenames should be prefixed, except when converted - * full path already starts with "\\?\". */ - if( osCygwin_conv_path(convertflag, zFilename, - zConverted+4, nByte)==0 ){ - if( (convertflag&CCP_RELATIVE) ){ - memmove(zConverted, zConverted+4, nByte); - }else if( memcmp(zConverted+4, L"\\\\", 4) ){ - memcpy(zConverted, L"\\\\?\\", 8); - }else if( zConverted[6]!='?' ){ - memmove(zConverted+6, zConverted+4, nByte); - memcpy(zConverted, L"\\\\?\\UNC", 14); - }else{ - memmove(zConverted, zConverted+4, nByte); + int nChar; + LPWSTR zWideFilename; + + if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename) + && winIsDirSep(zFilename[2])) ){ + i64 nByte; + int convertflag = CCP_POSIX_TO_WIN_W; + if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE; + nByte = (i64)osCygwin_conv_path(convertflag, + zFilename, 0, 0); + if( nByte>0 ){ + zConverted = sqlite3MallocZero(12+(u64)nByte); + if ( zConverted==0 ){ + return zConverted; + } + zWideFilename = zConverted; + /* Filenames should be prefixed, except when converted + * full path already starts with "\\?\". */ + if( osCygwin_conv_path(convertflag, zFilename, + zWideFilename+4, nByte)==0 ){ + if( (convertflag&CCP_RELATIVE) ){ + memmove(zWideFilename, zWideFilename+4, nByte); + }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){ + memcpy(zWideFilename, L"\\\\?\\", 8); + }else if( zWideFilename[6]!='?' ){ + memmove(zWideFilename+6, zWideFilename+4, nByte); + memcpy(zWideFilename, L"\\\\?\\UNC", 14); + }else{ + memmove(zWideFilename, zWideFilename+4, nByte); + } + return zConverted; } - return zConverted; + sqlite3_free(zConverted); } - sqlite3_free(zConverted); } + nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); + if( nChar==0 ){ + return 0; + } + zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 ); + if( zWideFilename==0 ){ + return 0; + } + nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, + zWideFilename, nChar); + if( nChar==0 ){ + sqlite3_free(zWideFilename); + zWideFilename = 0; + }else if( nChar>MAX_PATH + && winIsDriveLetterAndColon(zFilename) + && winIsDirSep(zFilename[2]) ){ + memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR)); + zWideFilename[2] = '\\'; + memcpy(zWideFilename, L"\\\\?\\", 8); + }else if( nChar>MAX_PATH + && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1]) + && zFilename[2] != '?' ){ + memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR)); + memcpy(zWideFilename, L"\\\\?\\UNC", 14); + } + zConverted = zWideFilename; +#else + zConverted = winUtf8ToUnicode(zFilename); +#endif /* __CYGWIN__ */ } - nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); - if( nChar==0 ){ - return 0; - } - zConverted = sqlite3MallocZero( ((u64)nChar+6)*sizeof(WCHAR) ); - if( zConverted==0 ){ - return 0; +#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32) + else{ + zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); } - nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, - zConverted, nChar); - if( nChar==0 ){ - sqlite3_free(zConverted); - zConverted = 0; - }else if( nChar>MAX_PATH - && winIsDriveLetterAndColon(zFilename) - && winIsDirSep(zFilename[2]) ){ - memmove(zConverted+4, zConverted, nChar*sizeof(WCHAR)); - zConverted[2] = '\\'; - memcpy(zConverted, L"\\\\?\\", 8); - }else if( nChar>MAX_PATH - && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1]) - && zFilename[2] != '?' ){ - memmove(zConverted+6, zConverted, nChar*sizeof(WCHAR)); - memcpy(zConverted, L"\\\\?\\UNC", 14); - } -#else /* if !defined(__CYGWIN__) */ - zConverted = winUtf8ToUnicode(zFilename); -#endif /* __CYGWIN__ */ +#endif /* caller will handle out of memory */ return zConverted; } @@ -3192,14 +4302,29 @@ static int winHandleOpen( /* TODO: platforms. ** TODO: retry-on-ioerr. */ - h = osCreateFileW(zConverted, /* lpFileName */ - (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ - FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ - NULL, /* lpSecurityAttributes */ - OPEN_ALWAYS, /* dwCreationDisposition */ - FILE_ATTRIBUTE_NORMAL|flag_overlapped, - NULL - ); + if( osIsNT() ){ + h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ + (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ + FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_ALWAYS, /* dwCreationDisposition */ + FILE_ATTRIBUTE_NORMAL|flag_overlapped, + NULL + ); + }else{ + /* Due to pre-processor directives earlier in this file, + ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ +#ifdef SQLITE_WIN32_HAS_ANSI + h = osCreateFileA((LPCSTR)zConverted, + (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ + FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_ALWAYS, /* dwCreationDisposition */ + FILE_ATTRIBUTE_NORMAL|flag_overlapped, + NULL + ); +#endif + } if( h==INVALID_HANDLE_VALUE ){ if( bReadonly==0 ){ @@ -3694,9 +4819,12 @@ static int winShmMap( #ifdef SQLITE_UWP hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); -#else +#elif defined(SQLITE_WIN32_HAS_WIDE) hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA + hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); #endif + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%lld, rc=%s\n", osGetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); @@ -3846,10 +4974,14 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ #endif #ifdef SQLITE_UWP pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); -#else +#elif defined(SQLITE_WIN32_HAS_WIDE) pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, (DWORD)((nMap>>32) & 0xffffffff), (DWORD)(nMap & 0xffffffff), NULL); +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA + pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect, + (DWORD)((nMap>>32) & 0xffffffff), + (DWORD)(nMap & 0xffffffff), NULL); #endif if( pFd->hMap==NULL ){ pFd->lastErrno = osGetLastError(); @@ -4201,7 +5333,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ } #endif - else{ + else if( osIsNT() ){ char *zMulti; LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) ); if( !zWidePath ){ @@ -4227,6 +5359,32 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ return SQLITE_IOERR_NOMEM_BKPT; } } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + char *zUtf8; + char *zMbcsPath = sqlite3MallocZero( nMax ); + if( !zMbcsPath ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM_BKPT; + } + if( osGetTempPathA(nMax, zMbcsPath)==0 ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); + return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), + "winGetTempname3", 0); + } + zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI()); + if( zUtf8 ){ + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); + sqlite3_free(zUtf8); + }else{ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM_BKPT; + } + } +#endif /* SQLITE_WIN32_HAS_ANSI */ /* ** Check to make sure the temporary directory ends with an appropriate @@ -4284,16 +5442,23 @@ static int winIsDir(LPCWSTR zConverted){ DWORD attr; int rc = 0; DWORD lastErrno; - int cnt = 0; - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW(zConverted, - GetFileExInfoStandard, - &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} - if( !rc ){ - return 0; /* Invalid name? */ + + if( osIsNT() ){ + int cnt = 0; + WIN32_FILE_ATTRIBUTE_DATA sAttrData; + memset(&sAttrData, 0, sizeof(sAttrData)); + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, + GetFileExInfoStandard, + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} + if( !rc ){ + return 0; /* Invalid name? */ + } + attr = sAttrData.dwFileAttributes; +#if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI) + }else{ + attr = osGetFileAttributesA((char*)zConverted); +#endif } - attr = sAttrData.dwFileAttributes; return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); } @@ -4327,6 +5492,9 @@ static int winOpen( DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; +#if SQLITE_OS_WINCE + int isTemp = 0; +#endif winVfsAppData *pAppData; winFile *pFile = (winFile*)id; LPWSTR zConverted; /* Filename in OS encoding */ @@ -4340,7 +5508,7 @@ static int winOpen( char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ -#if !defined(NDEBUG) +#if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0x0FFF00; /* Type of file to open */ #endif @@ -4455,28 +5623,61 @@ static int winOpen( } if( isDelete ){ +#if SQLITE_OS_WINCE + dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; + isTemp = 1; +#else dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE; +#endif }else{ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; } - do{ - h = osCreateFileW(zConverted, - dwDesiredAccess, - dwShareMode, NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ - int rc2; - sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); - sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } - }while( winRetryIoerr(&cnt, &lastErrno) ); + /* Reports from the internet are that performance is always + ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ +#if SQLITE_OS_WINCE + dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; +#endif + + if( osIsNT() ){ + do{ + h = osCreateFileW((LPCWSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ + int rc2; + sqlite3BeginBenignMalloc(); + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); + sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } + }while( winRetryIoerr(&cnt, &lastErrno) ); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + do{ + h = osCreateFileA((LPCSTR)zConverted, + dwDesiredAccess, + dwShareMode, NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL); + if( h!=INVALID_HANDLE_VALUE ) break; + if( isReadWrite ){ + int rc2; + sqlite3BeginBenignMalloc(); + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); + sqlite3EndBenignMalloc(); + if( rc2==SQLITE_OK && isRO ) break; + } + }while( winRetryIoerr(&cnt, &lastErrno) ); + } +#endif winLogIoerr(cnt, __LINE__); OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, @@ -4511,7 +5712,27 @@ static int winOpen( pAppData = (winVfsAppData*)pVfs->pAppData; - sqlite3_free(zConverted); +#if SQLITE_OS_WINCE + { + if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB + && ((pAppData==NULL) || !pAppData->bNoLock) + && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK + ){ + osCloseHandle(h); + sqlite3_free(zConverted); + sqlite3_free(zTmpname); + OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); + return rc; + } + } + if( isTemp ){ + pFile->zDeleteOnClose = zConverted; + }else +#endif + { + sqlite3_free(zConverted); + } + sqlite3_free(zTmpname); id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; pFile->pVfs = pVfs; @@ -4570,31 +5791,62 @@ static int winDelete( OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM_BKPT; } - do { - attr = osGetFileAttributesW(zConverted); - if ( attr==INVALID_FILE_ATTRIBUTES ){ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; + if( osIsNT() ){ + do { + attr = osGetFileAttributesW(zConverted); + if ( attr==INVALID_FILE_ATTRIBUTES ){ + lastErrno = osGetLastError(); + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ + }else{ + rc = SQLITE_ERROR; + } + break; } - break; - } - if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ - rc = SQLITE_ERROR; /* Files only. */ - break; - } - if ( osDeleteFileW(zConverted) ){ - rc = SQLITE_OK; /* Deleted OK. */ - break; - } - if ( !winRetryIoerr(&cnt, &lastErrno) ){ - rc = SQLITE_ERROR; /* No more retries. */ - break; - } - } while(1); + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ + rc = SQLITE_ERROR; /* Files only. */ + break; + } + if ( osDeleteFileW(zConverted) ){ + rc = SQLITE_OK; /* Deleted OK. */ + break; + } + if ( !winRetryIoerr(&cnt, &lastErrno) ){ + rc = SQLITE_ERROR; /* No more retries. */ + break; + } + } while(1); + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + do { + attr = osGetFileAttributesA(zConverted); + if ( attr==INVALID_FILE_ATTRIBUTES ){ + lastErrno = osGetLastError(); + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ + }else{ + rc = SQLITE_ERROR; + } + break; + } + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ + rc = SQLITE_ERROR; /* Files only. */ + break; + } + if ( osDeleteFileA(zConverted) ){ + rc = SQLITE_OK; /* Deleted OK. */ + break; + } + if ( !winRetryIoerr(&cnt, &lastErrno) ){ + rc = SQLITE_ERROR; /* No more retries. */ + break; + } + } while(1); + } +#endif if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); }else{ @@ -4616,11 +5868,9 @@ static int winAccess( ){ DWORD attr; int rc = 0; - int cnt = 0; DWORD lastErrno = 0; LPWSTR zConverted; int noRetry = 0; /* Do not use winRetryIoerr() */ - WIN32_FILE_ATTRIBUTE_DATA sAttrData; UNUSED_PARAMETER(pVfs); if( (flags & NORETRY)!=0 ){ @@ -4644,35 +5894,44 @@ static int winAccess( OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM_BKPT; } - memset(&sAttrData, 0, sizeof(sAttrData)); - while( !(rc = osGetFileAttributesExW(zConverted, - GetFileExInfoStandard, - &sAttrData)) - && !noRetry - && winRetryIoerr(&cnt, &lastErrno) - ){ /* Loop until true */} - if( rc ){ - /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file - ** as if it does not exist. - */ - if( flags==SQLITE_ACCESS_EXISTS - && sAttrData.nFileSizeHigh==0 - && sAttrData.nFileSizeLow==0 ){ - attr = INVALID_FILE_ATTRIBUTES; - }else{ - attr = sAttrData.dwFileAttributes; - } - }else{ - if( noRetry ) lastErrno = osGetLastError(); - winLogIoerr(cnt, __LINE__); - if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ - sqlite3_free(zConverted); - return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", - zFilename); + if( osIsNT() ){ + int cnt = 0; + WIN32_FILE_ATTRIBUTE_DATA sAttrData; + memset(&sAttrData, 0, sizeof(sAttrData)); + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, + GetFileExInfoStandard, + &sAttrData)) + && !noRetry + && winRetryIoerr(&cnt, &lastErrno) + ){ /* Loop until true */} + if( rc ){ + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. + */ + if( flags==SQLITE_ACCESS_EXISTS + && sAttrData.nFileSizeHigh==0 + && sAttrData.nFileSizeLow==0 ){ + attr = INVALID_FILE_ATTRIBUTES; + }else{ + attr = sAttrData.dwFileAttributes; + } }else{ - attr = INVALID_FILE_ATTRIBUTES; + if( noRetry ) lastErrno = osGetLastError(); + winLogIoerr(cnt, __LINE__); + if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ + sqlite3_free(zConverted); + return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", + zFilename); + }else{ + attr = INVALID_FILE_ATTRIBUTES; + } } } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + attr = osGetFileAttributesA((char*)zConverted); + } +#endif sqlite3_free(zConverted); switch( flags ){ case SQLITE_ACCESS_READ: @@ -4826,9 +6085,11 @@ static int winFullPathnameNoMutex( int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ +#if !SQLITE_OS_WINCE int nByte; LPWSTR zConverted; char *zOut; +#endif /* If this path name begins with "/X:" or "\\?\", where "X" is any ** alphabetic character, discard the initial "/" from the pathname. @@ -4845,23 +6106,22 @@ static int winFullPathnameNoMutex( zFull[nFull-1] = '\0'; if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){ int rc = SQLITE_OK; - int nLink = 1; /* Number of symbolic links followed so far */ - const char *zIn = zRelative; /* Input path for each iteration of loop */ + int nLink = 1; /* Number of symbolic links followed so far */ + const char *zIn = zRelative; /* Input path for each iteration of loop */ char *zDel = 0; struct stat buf; UNUSED_PARAMETER(pVfs); do { - /* Call lstat() on path zIn. Set bLink to true if the path - ** is a symbolic link, or false otherwise. */ + /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic + ** link, or false otherwise. */ int bLink = 0; if( osLstat && osReadlink ) { if( osLstat(zIn, &buf)!=0 ){ int myErrno = osErrno; if( myErrno!=ENOENT ){ - rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, - "lstat", zIn); + rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn); } }else{ bLink = ((buf.st_mode & 0170000) == 0120000); @@ -4878,8 +6138,7 @@ static int winFullPathnameNoMutex( if( rc==SQLITE_OK ){ nByte = osReadlink(zIn, zDel, nFull-1); if( nByte ==(DWORD)-1 ){ - rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, - "readlink", zIn); + rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn); }else{ if( zDel[0]!='/' ){ int n; @@ -4915,6 +6174,26 @@ static int winFullPathnameNoMutex( } #endif /* __CYGWIN__ */ +#if SQLITE_OS_WINCE && defined(_WIN32) + SimulateIOError( return SQLITE_ERROR ); + /* WinCE has no concept of a relative pathname, or so I am told. */ + /* WinRT has no way to convert a relative path to an absolute one. */ + if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ + /* + ** NOTE: We are dealing with a relative path name and the data + ** directory has been set. Therefore, use it as the basis + ** for converting the relative path name to an absolute + ** one by prepending the data directory and a backslash. + */ + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", + sqlite3_data_directory, winGetDirSep(), zRelative); + }else{ + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); + } + return SQLITE_OK; +#endif + +#if !SQLITE_OS_WINCE #if defined(_WIN32) /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this @@ -4938,7 +6217,7 @@ static int winFullPathnameNoMutex( if( zConverted==0 ){ return SQLITE_IOERR_NOMEM_BKPT; } - { + if( osIsNT() ){ LPWSTR zTemp; nByte = osGetFullPathNameW(zConverted, 0, 0, 0); if( nByte==0 ){ @@ -4963,6 +6242,32 @@ static int winFullPathnameNoMutex( zOut = winUnicodeToUtf8(zTemp); sqlite3_free(zTemp); } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + char *zTemp; + nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); + if( nByte==0 ){ + sqlite3_free(zConverted); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname3", zRelative); + } + zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) ); + if( zTemp==0 ){ + sqlite3_free(zConverted); + return SQLITE_IOERR_NOMEM_BKPT; + } + nByte = osGetFullPathNameA((char*)zConverted, nByte+3, zTemp, 0); + if( nByte==0 ){ + sqlite3_free(zConverted); + sqlite3_free(zTemp); + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), + "winFullPathname4", zRelative); + } + sqlite3_free(zConverted); + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI()); + sqlite3_free(zTemp); + } +#endif if( zOut ){ #ifdef __CYGWIN__ if( memcmp(zOut, "\\\\?\\", 4) ){ @@ -4989,6 +6294,7 @@ static int winFullPathnameNoMutex( }else{ return SQLITE_IOERR_NOMEM_BKPT; } +#endif } static int winFullPathname( sqlite3_vfs *pVfs, /* Pointer to vfs object */ @@ -5018,7 +6324,14 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); return 0; } - h = osLoadLibraryW ? osLoadLibraryW(zConverted) : 0; + if( osIsNT() ){ + h = osLoadLibraryW ? osLoadLibraryW((LPCWSTR)zConverted) : 0; + } +#ifdef SQLITE_WIN32_HAS_ANSI + else{ + h = osLoadLibraryA ? osLoadLibraryA((char*)zConverted) : 0; + } +#endif OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h)); sqlite3_free(zConverted); return (void*)h; @@ -5095,7 +6408,11 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ xorMemory(&e, (unsigned char*)&pid, sizeof(pid)); } { +#if SQLITE_WIN32_HAS_GETTICKCOUNT64 ULONGLONG cnt = osGetTickCount64(); +#else + ULONGLONG cnt = (ULONGLONG)osGetTickCount(); +#endif xorMemory(&e, (unsigned char*)&cnt, sizeof(cnt)); } { @@ -5103,7 +6420,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ osQueryPerformanceCounter(&i); xorMemory(&e, (unsigned char*)&i, sizeof(i)); } -#if SQLITE_WIN32_USE_UUID +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID #ifdef SQLITE_UWP # error SQLITE_WIN32_USE_UUID is incompatible with SQLITE_UWP #endif @@ -5116,7 +6433,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ osUuidCreateSequential(&id); xorMemory(&e, (unsigned char*)&id, sizeof(id)); } -#endif /* SQLITE_WIN32_USE_UUID */ +#endif /* !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID */ return e.nXor>nBuf ? nBuf : e.nXor; #endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */ } @@ -5164,7 +6481,17 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; +#if SQLITE_OS_WINCE + SYSTEMTIME time; + osGetSystemTime(&time); + /* if SystemTimeToFileTime() fails, it returns zero. */ + if (!osSystemTimeToFileTime(&time,&ft)){ + return SQLITE_ERROR; + } +#else osGetSystemTimeAsFileTime( &ft ); +#endif + *piNow = winFiletimeEpoch + ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; @@ -5258,6 +6585,7 @@ int sqlite3_os_init(void){ winGetSystemCall, /* xGetSystemCall */ winNextSystemCall, /* xNextSystemCall */ }; +#if defined(SQLITE_WIN32_HAS_WIDE) static sqlite3_vfs winLongPathVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ @@ -5282,6 +6610,7 @@ int sqlite3_os_init(void){ winGetSystemCall, /* xGetSystemCall */ winNextSystemCall, /* xNextSystemCall */ }; +#endif static sqlite3_vfs winNolockVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ @@ -5306,6 +6635,7 @@ int sqlite3_os_init(void){ winGetSystemCall, /* xGetSystemCall */ winNextSystemCall, /* xNextSystemCall */ }; +#if defined(SQLITE_WIN32_HAS_WIDE) static sqlite3_vfs winLongPathNolockVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ @@ -5330,18 +6660,22 @@ int sqlite3_os_init(void){ winGetSystemCall, /* xGetSystemCall */ winNextSystemCall, /* xNextSystemCall */ }; +#endif /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==60 ); + assert( ArraySize(aSyscall)==87 ); assert( strcmp(aSyscall[0].zName,"AreFileApisANSI")==0 ); - assert( strcmp(aSyscall[8].zName,"FreeLibrary")==0 ); - assert( strcmp(aSyscall[16].zName,"GetSystemInfo")==0 ); - assert( strcmp(aSyscall[24].zName,"HeapReAlloc")==0 ); - assert( strcmp(aSyscall[32].zName,"MapViewOfFileFromApp")==0 ); - assert( strcmp(aSyscall[40].zName,"UnmapViewOfFile")==0 ); - assert( strcmp(aSyscall[48].zName,"UuidCreate")==0 ); - assert( strcmp(aSyscall[56].zName,"lstat")==0 ); + assert( strcmp(aSyscall[8].zName,"CreateFileMappingW")==0 ); + assert( strcmp(aSyscall[16].zName,"FormatMessageW")==0 ); + assert( strcmp(aSyscall[24].zName,"GetFileSize")==0 ); + assert( strcmp(aSyscall[32].zName,"GetSystemTimeAsFileTime")==0 ); + assert( strcmp(aSyscall[40].zName,"HeapCreate")==0 ); + assert( strcmp(aSyscall[48].zName,"LoadLibraryW")==0 ); + assert( strcmp(aSyscall[56].zName,"ReadFile")==0 ); + assert( strcmp(aSyscall[64].zName,"UnmapViewOfFile")==0 ); + assert( strcmp(aSyscall[72].zName,"GetProcessHeap")==0 ); + assert( strcmp(aSyscall[80].zName,"getenv")==0 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); @@ -5350,9 +6684,17 @@ int sqlite3_os_init(void){ assert( winSysInfo.dwPageSize>0 ); sqlite3_vfs_register(&winVfs, 1); + +#if defined(SQLITE_WIN32_HAS_WIDE) sqlite3_vfs_register(&winLongPathVfs, 0); +#endif + sqlite3_vfs_register(&winNolockVfs, 0); + +#if defined(SQLITE_WIN32_HAS_WIDE) sqlite3_vfs_register(&winLongPathNolockVfs, 0); +#endif + #ifndef SQLITE_OMIT_WAL winBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); #endif diff --git a/src/os_win.h b/src/os_win.h index a5160ffc39..696486c19c 100644 --- a/src/os_win.h +++ b/src/os_win.h @@ -27,11 +27,53 @@ # include /* amalgamator: dontcache */ #endif +/* +** Determine if we are dealing with Windows NT. +** +** We ought to be able to determine if we are compiling for Windows 9x or +** Windows NT using the _WIN32_WINNT macro as follows: +** +** #if defined(_WIN32_WINNT) +** # define SQLITE_OS_WINNT 1 +** #else +** # define SQLITE_OS_WINNT 0 +** #endif +** +** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as +** it ought to, so the above test does not work. We'll just assume that +** everything is Windows NT unless the programmer explicitly says otherwise +** by setting SQLITE_OS_WINNT to 0. +*/ +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) +# define SQLITE_OS_WINNT 1 +#endif + +/* +** Determine if we are dealing with Windows CE - which has a much reduced +** API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif + +/* +** For WinCE, some API function parameters do not appear to be declared as +** volatile. +*/ +#if SQLITE_OS_WINCE +# define SQLITE_WIN32_VOLATILE +#else +# define SQLITE_WIN32_VOLATILE volatile +#endif + /* ** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() ** functions are not available (e.g. those not using MSVC, Cygwin, etc). */ -#if SQLITE_OS_WIN && SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) # define SQLITE_OS_WIN_THREADS 1 #else # define SQLITE_OS_WIN_THREADS 0 From 9b025c5122d91f2a2d675cc2c90069e182ae6bbb Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Sun, 11 Oct 2026 22:42:53 -0400 Subject: [PATCH 2/7] Fix calling-convention mismatch in the GetFileSizeEx/SetFilePointerEx shims The winGetFileSizeEx() and winSetFilePointerEx() wrappers that were restored to emulate the GetFileSizeEx()/SetFilePointerEx() APIs on Windows 9x, NT4 and 2000 were declared with the default (cdecl) calling convention, but the aSyscall[] thunk table invokes them through function pointers cast to WINAPI (__stdcall): #define osGetFileSizeEx \ ((BOOL(WINAPI*)(HANDLE,PLARGE_INTEGER))aSyscall[25].pCurrent) Calling a cdecl function through a stdcall pointer makes the callee return without popping its arguments, so the caller's stack stays eight bytes short. Every subsequent frame is then misaligned: in winFileSize() the address of the local LARGE_INTEGER passed to osGetFileSizeEx() was eight bytes off from the one the callee wrote to, so the file size was read back as garbage (a heap pointer rather than the real size) and the pager subsequently crashed with an access violation on the very first write transaction. Declare both shims WINAPI so they match the thunk table casts. Verified with an MSVC x86 build: CREATE TABLE, INSERT, SELECT and "PRAGMA journal_mode=WAL" now all succeed against a real database file. --- src/os_win.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/os_win.c b/src/os_win.c index 8da7243969..7edfd7b81e 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -515,8 +515,8 @@ static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; #endif #ifndef SQLITE_UWP -static BOOL winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize); -static BOOL winSetFilePointerEx( +static BOOL WINAPI winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize); +static BOOL WINAPI winSetFilePointerEx( HANDLE h, /* File handle */ LARGE_INTEGER liDistanceToMove, /* Number of bytes to move */ PLARGE_INTEGER pNewFilePointer, /* New file pointer - may be NULL */ @@ -1257,7 +1257,7 @@ static struct win_syscall { ** the older GetFileSize() and SetFilePointer() interfaces. Both routines ** return non-zero upon success and zero upon failure. */ -static BOOL winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize){ +static BOOL WINAPI winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize){ DWORD szHigh = 0; DWORD szLow = osGetFileSize(h, &szHigh); if( szLow==0xFFFFFFFF && osGetLastError()!=NO_ERROR ) return 0; @@ -1266,7 +1266,7 @@ static BOOL winGetFileSizeEx(HANDLE h, PLARGE_INTEGER pFileSize){ return 1; } -static BOOL winSetFilePointerEx( +static BOOL WINAPI winSetFilePointerEx( HANDLE h, /* File handle */ LARGE_INTEGER liDistanceToMove, /* Number of bytes to move */ PLARGE_INTEGER pNewFilePointer, /* New file pointer - may be NULL */ From 9fdf6d1ae3bbd4f7ae3d6bea58679b4688620bbf Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Sun, 11 Oct 2026 22:48:28 -0400 Subject: [PATCH 3/7] Fix LPWSTR/LPCSTR type mismatch in winDelete()'s ANSI branch winDelete() declares its converted filename as LPWSTR, but winConvertFromUtf8Filename() only returns a wide string when osIsNT() is true. On Windows 9x/ME -- where the ANSI API set is used instead -- it returns a narrow char* that was being handed to the A-suffixed osGetFileAttributesA() and osDeleteFileA() thunks without a cast, which compilers flag as an incompatible pointer type and which relies on the runtime representation happening to match. Cast the converted filename at each use site, matching what winOpen() already does in both of its branches. This is the last warning from a -Wall gcc build of the restored VFS, so the legacy configurations now compile cleanly: default (_WIN32_WINNT=0x0502) OK XP (_WIN32_WINNT=0x0501) OK Vista (_WIN32_WINNT=0x0601) OK Win9x (_WIN32_WINNT=0x0400, SQLITE_OS_WINNT=0, OMIT_WAL, no mmap) OK Verified with an MSVC x86 build: CREATE TABLE, INSERT, SELECT and "PRAGMA journal_mode=WAL" all succeed. --- src/os_win.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/os_win.c b/src/os_win.c index 7edfd7b81e..9d06462546 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -5793,7 +5793,7 @@ static int winDelete( } if( osIsNT() ){ do { - attr = osGetFileAttributesW(zConverted); + attr = osGetFileAttributesW((LPCWSTR)zConverted); if ( attr==INVALID_FILE_ATTRIBUTES ){ lastErrno = osGetLastError(); if( lastErrno==ERROR_FILE_NOT_FOUND @@ -5808,7 +5808,7 @@ static int winDelete( rc = SQLITE_ERROR; /* Files only. */ break; } - if ( osDeleteFileW(zConverted) ){ + if ( osDeleteFileW((LPCWSTR)zConverted) ){ rc = SQLITE_OK; /* Deleted OK. */ break; } @@ -5821,7 +5821,7 @@ static int winDelete( #ifdef SQLITE_WIN32_HAS_ANSI else{ do { - attr = osGetFileAttributesA(zConverted); + attr = osGetFileAttributesA((LPCSTR)zConverted); if ( attr==INVALID_FILE_ATTRIBUTES ){ lastErrno = osGetLastError(); if( lastErrno==ERROR_FILE_NOT_FOUND @@ -5836,7 +5836,7 @@ static int winDelete( rc = SQLITE_ERROR; /* Files only. */ break; } - if ( osDeleteFileA(zConverted) ){ + if ( osDeleteFileA((LPCSTR)zConverted) ){ rc = SQLITE_OK; /* Deleted OK. */ break; } From 9b5d0aac3904fee668441df4afdf792c1dcf8c08 Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Mon, 12 Oct 2026 00:03:45 -0400 Subject: [PATCH 4/7] Add a standalone smoke test for the Windows VFS, including the Win9x paths test/testopen.c is a small self-contained C program that drives the Windows VFS end to end: CREATE TABLE, INSERT, UPDATE, SELECT, DELETE, REINDEX, VACUUM, PRAGMA integrity_check and, with --wal, a trip through WAL mode. Every database call is wrapped in a structured exception filter that prints the exception code, faulting address and whether the fault was a read or a write. That is the only way to get anything useful out of the calling-convention bugs this work has been chasing; without it an access violation inside the VFS just kills the process with no diagnostic at all, which is how the GetFileSizeEx mismatch first showed up. The interesting option is --ansi. The restored Windows 9x/ME and NT4/2000 code paths in os_win.c are all selected by osIsNT(), and on any Windows release from XP onward osIsNT() is the compile-time constant 1, so the A-suffixed branches are unreachable on a modern machine and the normal test suite never touches them. os_win.c already anticipates this: it declares sqlite3_os_type as a process global rather than a static when SQLITE_TEST is defined, and the comment above it notes that the test fixture "can manually set this value to 1 to emulate Win98 behavior". Setting it to 1 before the first database access makes osIsNT() false and routes everything through the ANSI APIs. SQLITE_WIN32_GETVERSIONEX=1 is required alongside it, because on modern SDKs GetVersionEx() is deprecated and SQLITE_WIN32_GETVERSIONEX therefore defaults to 0, which makes osIsNT() the literal (1) and bypasses sqlite3_os_type entirely. Building with SQLITE_WIN32_NO_WIDE does NOT work for this and is not used here. It leaves SQLITE_WIN32_HAS_WIDE undefined, which zeroes out the W-suffixed syscall entries, but it does not change the value of SQLITE_WIN32_GETVERSIONEX, so osIsNT() still returns 1 and the first call into the NT branch reaches a now-NULL GetFullPathNameW thunk and faults at address 0. Both variants build warning-free with MSVC x86 and pass: cl -DSQLITE_OS_WIN=1 -DSQLITE_THREADSAFE=1 ... ALL PASSED cl ... -DSQLITE_TEST -DSQLITE_WIN32_GETVERSIONEX=1 ... ALL PASSED The second of those is the first runtime exercise the restored legacy code paths have ever had. --- test/testopen.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 test/testopen.c diff --git a/test/testopen.c b/test/testopen.c new file mode 100644 index 0000000000..c29f5b004a --- /dev/null +++ b/test/testopen.c @@ -0,0 +1,225 @@ +/* +** 2026 +** +** 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. +** +************************************************************************* +** +** A small standalone smoke test for the Windows VFS layer, with an +** emphasis on the legacy Windows code paths. +** +** The restored Windows 9x/ME and NT4/2000 code paths in os_win.c are +** selected at run time by osIsNT(). On any Windows release from XP +** onward osIsNT() always returns true, so the ANSI (A-suffixed) branches +** are dead code there and are never exercised by the normal test suite. +** +** os_win.c exports the process-global sqlite3_os_type variable when it is +** built with SQLITE_TEST, and the comment there notes that the test fixture +** "can manually set this value to 1 to emulate Win98 behavior". That is +** the switch used here. SQLITE_WIN32_GETVERSIONEX=1 is also required, +** because on modern SDKs GetVersionEx() is deprecated and SQLITE_WIN32_ +** GETVERSIONEX therefore defaults to 0, which makes osIsNT() the constant +** (1) and bypasses sqlite3_os_type entirely. +** +** Build the normal (NT) variant: +** +** cl -DSQLITE_OS_WIN=1 -DSQLITE_THREADSAFE=1 -I. -Isrc \ +** test\testopen.c sqlite3.c /link /MACHINE:x86 +** +** Build the variant that can reach the legacy Win9x ANSI branches: +** +** cl -DSQLITE_OS_WIN=1 -DSQLITE_THREADSAFE=1 -DSQLITE_TEST \ +** -DSQLITE_WIN32_GETVERSIONEX=1 -I. -Isrc \ +** test\testopen.c sqlite3.c /link /MACHINE:x86 +** +** Run: +** +** testopen.exe [--ansi] [--wal] +** +** Options: +** +** --ansi Emulate Win98 by setting sqlite3_os_type=1 before the +** first database access. Requires SQLITE_TEST. +** --wal Also switch the database into WAL mode +*/ +#include +#include +#include +#include "sqlite3.h" + +#if defined(SQLITE_TEST) +/* +** Exported by os_win.c under SQLITE_TEST. 0 = unknown, 1 = Win9x, +** 2 = WinNT. Setting it to 1 before the first database access makes +** osIsNT() false and therefore routes all VFS work through the ANSI +** (A-suffixed) Windows APIs. +*/ +extern volatile long sqlite3_os_type; +#endif + +static int nFail = 0; + +static void fail(const char *zWhat, int rc, const char *zErr){ + printf("FAIL: %s: rc=%d %s\n", zWhat, rc, zErr?zErr:""); + fflush(stdout); + nFail++; +} + +static const char *exName(DWORD c){ + switch(c){ + case EXCEPTION_ACCESS_VIOLATION: return "ACCESS_VIOLATION"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL_INSTRUCTION"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "INT_DIVIDE_BY_ZERO"; + case EXCEPTION_STACK_OVERFLOW: return "STACK_OVERFLOW"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_OVERFLOW: return "FLT_OVERFLOW"; + default: return "OTHER"; + } +} + +/* +** Structured exception filter. Without this an access violation inside +** the VFS simply kills the process and reports nothing useful, which is +** exactly how the GetFileSizeEx/SetFilePointerEx calling-convention bug +** first showed up. +*/ +static int WINAPI sehFilter(EXCEPTION_POINTERS *ep){ + DWORD code = ep->ExceptionRecord->ExceptionCode; + printf("\n*** EXCEPTION %s (0x%08lX) at addr=%p", exName(code), + (unsigned long)code, ep->ExceptionRecord->ExceptionAddress); + if( code==EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters>=2 ){ + printf(" %s of addr=%p", + ep->ExceptionRecord->ExceptionInformation[0]?"WRITE":"READ", + (void*)ep->ExceptionRecord->ExceptionInformation[1]); + } + printf("\n"); + fflush(stdout); + nFail++; + return EXCEPTION_EXECUTE_HANDLER; +} + +static int countCb(void *pNotUsed, int nArg, char **azArg, char **azCol){ + int i; + (void)pNotUsed; + for(i=0; i\n", argv[0]); + return 1; + } + + printf("sqlite3 %s (%d-bit)\n", sqlite3_libversion(), + (int)(sizeof(void*)*8)); + if( bAnsi ){ +#if defined(SQLITE_TEST) + /* + ** Must be done before anything touches the VFS. osIsNT() evaluates + ** ((sqlite3_os_type==2) || sqlite3_win32_is_nt()), and + ** sqlite3_win32_is_nt() short-circuits on a non-zero sqlite3_os_type, + ** so a value of 1 makes osIsNT() false. + */ + sqlite3_os_type = 1; + printf("mode: Win98 emulation, legacy ANSI code paths (osIsNT()==0)\n"); +#else + printf("mode: --ansi requires the amalgamation to be built with " + "-DSQLITE_TEST -DSQLITE_WIN32_GETVERSIONEX=1\n"); + return 1; +#endif + }else{ + printf("mode: default (NT code paths)\n"); + } + fflush(stdout); + + __try{ + rc = sqlite3_open(zDb, &db); + }__except(sehFilter(GetExceptionInformation())){ + printf("crashed in sqlite3_open\n"); + return 2; + } + printf("sqlite3_open rc=%d\n", rc); + fflush(stdout); + if( rc!=SQLITE_OK ){ + fail("sqlite3_open", rc, db?sqlite3_errmsg(db):0); + sqlite3_close(db); + return 1; + } + + run(db, "CREATE TABLE", + "CREATE TABLE t(a INTEGER PRIMARY KEY, b TEXT, c BLOB);"); + run(db, "INSERT", "INSERT INTO t(b,c) VALUES('one',x'001122')," + "('two',x'334455'),('three',x'66778899');"); + run(db, "UPDATE", "UPDATE t SET b=b||'-x' WHERE a=2;"); + run(db, "SELECT", "SELECT a,b,hex(c) FROM t ORDER BY a;"); + run(db, "DELETE", "DELETE FROM t WHERE a=3;"); + run(db, "SELECT after DELETE", "SELECT count(*),group_concat(b) FROM t;"); + run(db, "REINDEX", "REINDEX;"); + run(db, "VACUUM", "VACUUM;"); + run(db, "PRAGMA integrity_check", "PRAGMA integrity_check;"); + + if( bWal ){ + run(db, "journal_mode=WAL", "PRAGMA journal_mode=WAL;"); + run(db, "INSERT in WAL", "INSERT INTO t(b) VALUES('wal');"); + run(db, "SELECT in WAL", "SELECT count(*) FROM t;"); + run(db, "journal_mode=DELETE", "PRAGMA journal_mode=DELETE;"); + } + + __try{ + rc = sqlite3_close(db); + }__except(sehFilter(GetExceptionInformation())){ + printf("crashed in sqlite3_close\n"); + return 3; + } + printf("sqlite3_close rc=%d\n", rc); + if( rc!=SQLITE_OK ) fail("sqlite3_close", rc, 0); + + if( nFail==0 ){ + printf("ALL PASSED\n"); + return 0; + } + printf("%d FAILURE(S)\n", nFail); + return 1; +} From 422344d1cd407c966a9725a157dda7f394713d7a Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Mon, 12 Oct 2026 00:37:14 -0400 Subject: [PATCH 5/7] Stop GetNativeSystemInfo from breaking Win9x loads GetNativeSystemInfo first appeared in Windows XP, but its entry in the aSyscall[] table was the only one with no availability guard. Because the table takes the address of every syscall at static initialisation, the linker adds GetNativeSystemInfo to the executable's import table whether or not any code path calls it, so a binary built for Windows 9x/ME or NT4 refuses to load there at all: the loader cannot satisfy the import before main() ever runs. A runtime guard inside the table is not sufficient for that reason. It was also dead weight. osGetNativeSystemInfo is defined but never referenced anywhere in the tree, so removing the import costs nothing. Add SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO alongside the existing SQLITE_WIN32_HAS_GETTICKCOUNT64, defaulting to true when _WIN32_WINNT is at least 0x0501 or is not specified at all, and use it to select between the real symbol and a NULL thunk. The entry stays at index 69 so no renumbering is required. Verified with i686-w64-mingw32-gcc 8.1.0, which is the toolchain that matters here because it links msvcrt.dll rather than the Vista-and-later ucrtbase. Building shell.c + sqlite3.c for three targets gives 0 errors, and objdump of the resulting import tables shows: _WIN32_WINNT=0x0400 GetNativeSystemInfo absent _WIN32_WINNT=0x0501 GetNativeSystemInfo present _WIN32_WINNT=0x0600 GetNativeSystemInfo present, GetTickCount64 present The 0x0400 binary now imports only KERNEL32, msvcrt.dll and USER32, and carries an OS version of 4.0 in its PE header. All three targets still build warning-free and run CREATE/INSERT/SELECT plus WAL mode. The MSVC build is unaffected because its SDK sets NTDDI_VERSION at or above NTDDI_WINBLUE, which selects the same table entry as before. Confirmed with an MSVC x86 build of test/testopen.c in both the default and Win98-emulation modes, tclsh tool/renumber-win-thunk.tcl src/os_win.c, and 0 errors out of 13,780 testfixture tests. --- src/os_win.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/os_win.c b/src/os_win.c index 9d06462546..43c3cb040d 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -123,6 +123,29 @@ # endif #endif +/* +** Check to see if the GetNativeSystemInfo function is available. It first +** appeared in Windows XP, so it is unavailable when targeting earlier +** systems such as Windows 9x or Windows NT4. When no _WIN32_WINNT is +** specified, assume the most recent version of the SDK is in use. +** +** Taking the address of an unavailable function in the aSyscall[] table +** is enough to make the linker add it to the executable's import table, +** which prevents the executable from loading at all on a system that +** lacks the export -- the runtime guard in the table is not sufficient. +*/ +#ifndef SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO +# if defined(SQLITE_UWP) +# define SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO 1 +# elif defined(_WIN32_WCE) +# define SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO 0 +# elif !defined(_WIN32_WINNT) || _WIN32_WINNT>=0x0501 +# define SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO 1 +# else +# define SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO 0 +# endif +#endif + /* ** This constant should already be defined (in the "WinDef.h" SDK file). */ @@ -1086,7 +1109,11 @@ static struct win_syscall { #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ BOOL))aSyscall[68].pCurrent) +#if SQLITE_WIN32_HAS_GETNATIVESYSTEMINFO { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, +#else + { "GetNativeSystemInfo", (SYSCALL)0, 0 }, +#endif #define osGetNativeSystemInfo ((VOID(WINAPI*)( \ LPSYSTEM_INFO))aSyscall[69].pCurrent) From 7b3b18041dc2401b5118f585e84f16c391497c46 Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Mon, 12 Oct 2026 01:06:23 -0400 Subject: [PATCH 6/7] Stop GetSystemTimeAsFileTime and TryEnterCriticalSection from breaking Win9x loads These are the same class of defect as GetNativeSystemInfo in the previous commit: an API that the target platform lacks ends up in the executable's import table anyway, so the loader refuses to start the program before any runtime guard inside SQLite can run. GetSystemTimeAsFileTime first appeared in Windows 98 and Windows NT 4.0, so Windows 95 does not have it. Its thunk table entry was guarded only by !SQLITE_OS_WINCE. Replace that with SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME. Distinguishing the two platforms needs care, because Windows 95 and Windows NT 4.0 both report _WIN32_WINNT as 0x0400. SQLITE_OS_WINNT is what separates them, so the macro turns off only for a build that both opts out of NT with SQLITE_OS_WINNT=0 and targets a version below 0x0410. That means every existing configuration keeps compiling the same code as before, and only an explicit Win95 build changes. The fallback is not new. winCurrentTimeInt64() already had one, written for Windows CE, that builds the FILETIME out of GetSystemTime plus SystemTimeToFileTime. Both of those are unconditional thunk entries and both exist on Windows 95, so the WinCE guard is simply widened to cover this case too, and the awkward "if it fails it returns zero" comment becomes a real check that returns SQLITE_ERROR. winRandomness() gained the same fallback. Unlike GetSystemTimeAsFileTime(), SystemTimeToFileTime() can fail, and the old code there would have XORed uninitialized stack bytes into the entropy pool, so it now skips the contribution on failure. TryEnterCriticalSection first appeared in Windows NT 4.0 Service Pack 3 and Windows 98, and was never in Windows 95. mutex_w32.c guarded its use with _WIN32_WINNT >= 0x0400, which is true of a Windows 95 build as well, so the code compiled in and dragged the import along even though the surrounding if( winMutex_isNt && ... ) could never be true there. Fold that condition together with SQLITE_OS_WINNT into a single SQLITE_MUTEX_HAVE_TRYENTER and guard the winMutex_isNt variable and the sqlite3_win32_is_nt() prototype with it too, since nothing else in the file uses them. Verified with i686-w64-mingw32-gcc 8.1.0. Note that a SQLITE_OS_WINNT=0 build must also pass SQLITE_OMIT_WAL=1 and SQLITE_MAX_MMAP_SIZE=0; that is an existing upstream requirement, since WAL and mmap rely on the NT kernel, and SQLite raises an #error if they are left on. All five of these configurations build with 0 errors and only the pre-existing sigterm_handler warning from shell.c: _WIN32_WINNT=0x0400 nt4 _WIN32_WINNT=0x0400 SQLITE_OS_WINNT=0 win95 _WIN32_WINNT=0x0410 SQLITE_OS_WINNT=0 win98 _WIN32_WINNT=0x0501 xp _WIN32_WINNT=0x0600 vista objdump of the import tables shows TryEnterCriticalSection absent from the win95 and win98 binaries and present in the other three, and all five run CREATE/INSERT/SELECT/VACUUM/integrity_check. GetTickCount64 appears only in the vista binary, as before. One caveat worth recording: GetSystemTimeAsFileTime is still listed as imported in every MinGW binary, including the win95 one. That is not SQLite's doing. A trivial program consisting of nothing but "int main(void){ return 0; }" built with the same toolchain imports it too, at the same hint number, so it comes from the MinGW CRT startup code. A MinGW-linked executable therefore cannot run on plain Windows 95 no matter what SQLite does. This change still removes SQLite's own dependence on the API and collapses the WinCE and Win95 fallbacks into one path, which is what matters for any other toolchain. The MSVC build is unaffected, since its SDK sets _WIN32_WINNT well above 0x0410. Confirmed with an MSVC x86 build of test/testopen.c in both the default and Win98-emulation modes, tclsh tool/renumber-win-thunk.tcl src/os_win.c, and 0 errors out of 13,780 testfixture tests. --- src/mutex_w32.c | 35 +++++++++++++++++++++++++-------- src/os_win.c | 52 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/mutex_w32.c b/src/mutex_w32.c index a3bf3537de..e54779d8d7 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -115,6 +115,26 @@ void sqlite3MemoryBarrier(void){ #endif } +/* +** The TryEnterCriticalSection() interface is only available on WinNT, +** and it did not appear until Windows NT 4.0 Service Pack 3. Some +** Windows compilers complain if you try to use it without first doing +** some #defines that prevent SQLite from building on Win98. See +** ticket #2685. +** +** Guarding on SQLITE_OS_WINNT as well as on the version macro is what +** keeps a Windows 9x build out of the executable's import table: the +** mere act of taking TryEnterCriticalSection's address in code that is +** compiled in is enough to make the linker import it, and Windows 95 +** has no such export to import, so the loader would refuse to start the +** executable before any runtime guard could run. +*/ +#if SQLITE_OS_WINNT && (!defined(_WIN32_WINNT) || _WIN32_WINNT >= 0x0400) +# define SQLITE_MUTEX_HAVE_TRYENTER 1 +#else +# define SQLITE_MUTEX_HAVE_TRYENTER 0 +#endif + /* ** Static mutexes. ** @@ -130,7 +150,9 @@ static ALIGN128 union staticMutexes { char spacer[128]; } aWindowsMutex[12]; static int winMutex_isInit = 0; +#if SQLITE_MUTEX_HAVE_TRYENTER static int winMutex_isNt = -1; /* <0 means "need to query" */ +#endif /* As the winMutexInit() and winMutexEnd() functions are called as part ** of the sqlite3_initialize() and sqlite3_shutdown() processing, the @@ -138,7 +160,9 @@ static int winMutex_isNt = -1; /* <0 means "need to query" */ */ static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; +#if SQLITE_MUTEX_HAVE_TRYENTER int sqlite3_win32_is_nt(void); /* os_win.c */ +#endif void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ static int winMutexInit(void){ @@ -331,15 +355,10 @@ static int winMutexTry(sqlite3_mutex *p){ /* ** The sqlite3_mutex_try() routine is very rarely used, and when it ** is used it is merely an optimization. So it is OK for it to always - ** fail. - ** - ** The TryEnterCriticalSection() interface is only available on WinNT. - ** And some windows compilers complain if you try to use it without - ** first doing some #defines that prevent SQLite from building on Win98. - ** For that reason, we will omit this optimization for now. See - ** ticket #2685. + ** fail. SQLITE_MUTEX_HAVE_TRYENTER is defined near the top of this + ** file, where the reason for the guard is spelled out. */ -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400 +#if SQLITE_MUTEX_HAVE_TRYENTER assert( winMutex_isInit==1 ); assert( winMutex_isNt>=-1 && winMutex_isNt<=1 ); if( winMutex_isNt<0 ){ diff --git a/src/os_win.c b/src/os_win.c index 43c3cb040d..651e12b4c2 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -146,6 +146,29 @@ # endif #endif +/* +** Check to see if the GetSystemTimeAsFileTime function is available. It +** first appeared in Windows 98 and Windows NT 4.0, so it is unavailable +** when targeting Windows 95. +** +** Windows 95 and Windows NT 4.0 both report _WIN32_WINNT as 0x0400, so +** they cannot be told apart by the version macro alone. SQLITE_OS_WINNT +** is what distinguishes them: only a build that explicitly opts out of NT +** with SQLITE_OS_WINNT=0 *and* targets a version below 0x0410 is a +** Windows 95 build. When no _WIN32_WINNT is specified, assume the most +** recent version of the SDK is in use. +*/ +#ifndef SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME +# if SQLITE_OS_WINCE +# define SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME 0 +# elif defined(SQLITE_OS_WINNT) && !SQLITE_OS_WINNT && \ + defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0410 +# define SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME 0 +# else +# define SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME 1 +# endif +#endif + /* ** This constant should already be defined (in the "WinDef.h" SDK file). */ @@ -824,7 +847,7 @@ static struct win_syscall { { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, #define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[31].pCurrent) -#if !SQLITE_OS_WINCE +#if SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, #else { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, @@ -6427,8 +6450,21 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ e.i = 0; { FILETIME x; +#if SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME osGetSystemTimeAsFileTime(&x); xorMemory(&e, (unsigned char*)&x, sizeof(x)); +#else + /* + ** Unlike GetSystemTimeAsFileTime(), SystemTimeToFileTime() can fail. + ** Contribute nothing on failure rather than mixing uninitialized + ** bytes into the entropy pool. + */ + SYSTEMTIME st; + osGetSystemTime(&st); + if( osSystemTimeToFileTime(&st, &x) ){ + xorMemory(&e, (unsigned char*)&x, sizeof(x)); + } +#endif } { DWORD pid = osGetCurrentProcessId(); @@ -6508,15 +6544,19 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; -#if SQLITE_OS_WINCE +#if SQLITE_WIN32_HAS_GETSYSTEMTIMEASFILETIME + osGetSystemTimeAsFileTime( &ft ); +#else + /* + ** GetSystemTimeAsFileTime() is not available on Windows CE or Windows + ** 95, so synthesize the FILETIME out of the SYSTEMTIME instead. If + ** SystemTimeToFileTime() fails, it returns zero. + */ SYSTEMTIME time; osGetSystemTime(&time); - /* if SystemTimeToFileTime() fails, it returns zero. */ - if (!osSystemTimeToFileTime(&time,&ft)){ + if( !osSystemTimeToFileTime(&time,&ft) ){ return SQLITE_ERROR; } -#else - osGetSystemTimeAsFileTime( &ft ); #endif *piNow = winFiletimeEpoch + From 8d5d67378efed102625933825edb452316205d2a Mon Sep 17 00:00:00 2001 From: Karthik Kumar Viswanathan Date: Mon, 12 Oct 2026 01:24:28 -0400 Subject: [PATCH 7/7] Add a Windows 95 build helper that defeats the MinGW CRT's Win98 import SQLite's own Windows code no longer imports anything introduced after Windows 95. A MinGW-W64 linked executable still could not load on Windows 95, though, because of the toolchain rather than the database engine. crt2.o, the MinGW C runtime startup, calls __security_init_cookie() on every program start in order to seed the stack-protector guard. The only definition of that function in the toolchain lives in an archive member of libmingw32.a -- lib32_libmingw32_a-gs_support.o -- that also imports GetSystemTimeAsFileTime, an API that first appeared in Windows 98. A static import has to be satisfied by the loader before main() runs, so the executable refused to start on Windows 95 regardless of what SQLite did. No compiler flag avoids it: the reference comes from crt2.o, not from user code, so -fno-stack-protector and friends make no difference. tool/win95gs.c defines the four symbols that archive member provides, so the linker resolves them locally and never extracts the member at all. The Win98-only import therefore never reaches the import table. The seed is built out of GetSystemTime, SystemTimeToFileTime, the process and thread ids, the tick count and the performance counter -- every one of which exists on Windows 95. Two details in there are load-bearing and worth recording, because getting either wrong fails silently by falling back to the archive member: * The C identifiers have two underscores, so that i386 cdecl decoration yields the three that crt2.o asks for. * They are cdecl, not WINAPI. A stdcall definition decorates to name@N and will not match crt2.o's reference. This file is not part of the amalgamation and no makefile references it, so an ordinary build is unaffected. It is linked in by hand, only for a build that is deliberately targeting Windows 95: i686-w64-mingw32-gcc -O2 -DSQLITE_OS_WIN=1 -DSQLITE_OS_WINNT=0 \ -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 \ -DSQLITE_OMIT_WAL=1 -DSQLITE_MAX_MMAP_SIZE=0 \ -o sqlite3.exe shell.c sqlite3.c tool/win95gs.c -ladvapi32 Verified with i686-w64-mingw32-gcc 8.1.0. objdump of the resulting import table shows it containing only KERNEL32, msvcrt.dll and USER32, with no GetSystemTimeAsFileTime, GetNativeSystemInfo, TryEnterCriticalSection or GetTickCount64, and the PE header still declaring OS version 4.0. The binary builds with 0 errors and runs CREATE TABLE, INSERT, SELECT, VACUUM and PRAGMA integrity_check. journal_mode=WAL correctly reports "delete", since Windows 95 has neither WAL nor memory mapped files and the build passes SQLITE_OMIT_WAL and SQLITE_MAX_MMAP_SIZE=0 accordingly. --- tool/win95gs.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tool/win95gs.c diff --git a/tool/win95gs.c b/tool/win95gs.c new file mode 100644 index 0000000000..f9819d35ed --- /dev/null +++ b/tool/win95gs.c @@ -0,0 +1,109 @@ +/* +** 2026 +** +** 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. +** +******************************************************************************* +** +** A drop-in replacement for the MinGW-W64 stack-protector support object, +** provided so that SQLite can be linked into an executable that loads on +** plain Windows 95. +** +** This file is not part of the SQLite amalgamation and is not referenced +** by any makefile. It is linked in by hand, and only for a build that is +** deliberately targeting Windows 95: +** +** i686-w64-mingw32-gcc -O2 -DSQLITE_OS_WIN=1 -DSQLITE_OS_WINNT=0 \ +** -D_WIN32_WINNT=0x0400 -DWINVER=0x0400 \ +** -DSQLITE_OMIT_WAL=1 -DSQLITE_MAX_MMAP_SIZE=0 \ +** -o sqlite3.exe shell.c sqlite3.c tool/win95gs.c -ladvapi32 +** +** Why this is needed +** ------------------ +** +** crt2.o, the MinGW C runtime startup, calls __security_init_cookie() on +** every program start to seed the stack-protector guard with time- and +** address-derived entropy. The only definition of that function in the +** toolchain lives in an archive member of libmingw32.a that also imports +** GetSystemTimeAsFileTime. That API first appeared in Windows 98, so +** Windows 95 has no such export to import. +** +** A static import has to be satisfied by the loader before main() runs, so +** the executable refuses to start on Windows 95 no matter what SQLite +** itself does -- and SQLite does not, by this point, need anything that +** Windows 95 lacks. No compiler flag avoids it, because the reference +** comes from crt2.o rather than from user code. +** +** Supplying our own definitions of the four symbols the archive member +** provides means the linker resolves them locally and never extracts the +** archive member at all, so the Win98-only import never reaches the +** executable's import table. +** +** Naming and calling convention +** ------------------------------ +** +** crt2.o asks for these by the exact undecorated names below. On i386 +** Windows a cdecl identifier "X" is decorated to "_X", so a C identifier +** of two underscores yields the three that the linker wants. They must +** be cdecl and not WINAPI: a stdcall definition is decorated to +** "name@N", which will not match crt2.o's reference, and the linker will +** silently fall back to the archive member -- which is exactly the bug +** this file exists to prevent. +** +** The seed is deliberately a function of the system time, the process and +** thread ids, the tick count and the performance counter, all of which +** exist on Windows 95. GetSystemTimeAsFileTime() is not used, nor is +** anything else introduced after Windows 95. +** +** What this does not do: Windows 95 also lacks WAL mode and memory mapped +** files, which is why the build above passes SQLITE_OMIT_WAL and +** SQLITE_MAX_MMAP_SIZE=0. SQLite raises an #error if those are left on +** for a SQLITE_OS_WINNT=0 build. +*/ +#include + +/* +** The stack-rotating cookies. The default guard value must never be +** used, so __security_init_cookie() below replaces it unconditionally +** and refuses the one value that would be indistinguishable from it. +*/ +unsigned long __security_cookie = 0xBB40E64EUL; +unsigned long __security_cookie_complement = 0x44BF19B1UL; + +/* +** Reached only if the stack-rotating guard was actually tripped. +*/ +void __report_gsfailure(void){ + TerminateProcess(GetCurrentProcess(), 0xC0000409UL); +} + +/* +** Called from crt2.o before the user's main(). Seeds the guard with a +** mixture of values that are all available on Windows 95. +*/ +void __security_init_cookie(void){ + FILETIME ft; + SYSTEMTIME st; + LARGE_INTEGER pc; + unsigned long c; + + GetSystemTime(&st); + if( SystemTimeToFileTime(&st, &ft) ){ + c = ft.dwLowDateTime ^ ft.dwHighDateTime; + }else{ + c = 0; + } + c ^= GetCurrentProcessId(); + c ^= GetCurrentThreadId(); + c ^= GetTickCount(); + QueryPerformanceCounter(&pc); + c ^= (unsigned long)(pc.LowPart ^ pc.HighPart); + if( c==0xBB40E64EUL ) c = 0xBB40E64DUL; + __security_cookie = c; + __security_cookie_complement = ~c; +}