diff options
Diffstat (limited to '')
-rwxr-xr-x | libraries/sqlite/win32/os_win.c | 1545 |
1 files changed, 1545 insertions, 0 deletions
diff --git a/libraries/sqlite/win32/os_win.c b/libraries/sqlite/win32/os_win.c new file mode 100755 index 0000000..bcb1c6e --- /dev/null +++ b/libraries/sqlite/win32/os_win.c | |||
@@ -0,0 +1,1545 @@ | |||
1 | /* | ||
2 | ** 2004 May 22 | ||
3 | ** | ||
4 | ** The author disclaims copyright to this source code. In place of | ||
5 | ** a legal notice, here is a blessing: | ||
6 | ** | ||
7 | ** May you do good and not evil. | ||
8 | ** May you find forgiveness for yourself and forgive others. | ||
9 | ** May you share freely, never taking more than you give. | ||
10 | ** | ||
11 | ****************************************************************************** | ||
12 | ** | ||
13 | ** This file contains code that is specific to windows. | ||
14 | */ | ||
15 | #include "sqliteInt.h" | ||
16 | #if OS_WIN /* This file is used for windows only */ | ||
17 | |||
18 | |||
19 | /* | ||
20 | ** A Note About Memory Allocation: | ||
21 | ** | ||
22 | ** This driver uses malloc()/free() directly rather than going through | ||
23 | ** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers | ||
24 | ** are designed for use on embedded systems where memory is scarce and | ||
25 | ** malloc failures happen frequently. Win32 does not typically run on | ||
26 | ** embedded systems, and when it does the developers normally have bigger | ||
27 | ** problems to worry about than running out of memory. So there is not | ||
28 | ** a compelling need to use the wrappers. | ||
29 | ** | ||
30 | ** But there is a good reason to not use the wrappers. If we use the | ||
31 | ** wrappers then we will get simulated malloc() failures within this | ||
32 | ** driver. And that causes all kinds of problems for our tests. We | ||
33 | ** could enhance SQLite to deal with simulated malloc failures within | ||
34 | ** the OS driver, but the code to deal with those failure would not | ||
35 | ** be exercised on Linux (which does not need to malloc() in the driver) | ||
36 | ** and so we would have difficulty writing coverage tests for that | ||
37 | ** code. Better to leave the code out, we think. | ||
38 | ** | ||
39 | ** The point of this discussion is as follows: When creating a new | ||
40 | ** OS layer for an embedded system, if you use this file as an example, | ||
41 | ** avoid the use of malloc()/free(). Those routines work ok on windows | ||
42 | ** desktops but not so well in embedded systems. | ||
43 | */ | ||
44 | |||
45 | #include <winbase.h> | ||
46 | |||
47 | #ifdef __CYGWIN__ | ||
48 | # include <sys/cygwin.h> | ||
49 | #endif | ||
50 | |||
51 | /* | ||
52 | ** Macros used to determine whether or not to use threads. | ||
53 | */ | ||
54 | #if defined(THREADSAFE) && THREADSAFE | ||
55 | # define SQLITE_W32_THREADS 1 | ||
56 | #endif | ||
57 | |||
58 | /* | ||
59 | ** Include code that is common to all os_*.c files | ||
60 | */ | ||
61 | #include "os_common.h" | ||
62 | |||
63 | /* | ||
64 | ** Determine if we are dealing with WindowsCE - which has a much | ||
65 | ** reduced API. | ||
66 | */ | ||
67 | #if defined(_WIN32_WCE) | ||
68 | # define OS_WINCE 1 | ||
69 | # define AreFileApisANSI() 1 | ||
70 | #else | ||
71 | # define OS_WINCE 0 | ||
72 | #endif | ||
73 | |||
74 | /* | ||
75 | ** WinCE lacks native support for file locking so we have to fake it | ||
76 | ** with some code of our own. | ||
77 | */ | ||
78 | #if OS_WINCE | ||
79 | typedef struct winceLock { | ||
80 | int nReaders; /* Number of reader locks obtained */ | ||
81 | BOOL bPending; /* Indicates a pending lock has been obtained */ | ||
82 | BOOL bReserved; /* Indicates a reserved lock has been obtained */ | ||
83 | BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ | ||
84 | } winceLock; | ||
85 | #endif | ||
86 | |||
87 | /* | ||
88 | ** The winFile structure is a subclass of sqlite3_file* specific to the win32 | ||
89 | ** portability layer. | ||
90 | */ | ||
91 | typedef struct winFile winFile; | ||
92 | struct winFile { | ||
93 | const sqlite3_io_methods *pMethod;/* Must be first */ | ||
94 | HANDLE h; /* Handle for accessing the file */ | ||
95 | unsigned char locktype; /* Type of lock currently held on this file */ | ||
96 | short sharedLockByte; /* Randomly chosen byte used as a shared lock */ | ||
97 | #if OS_WINCE | ||
98 | WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ | ||
99 | HANDLE hMutex; /* Mutex used to control access to shared lock */ | ||
100 | HANDLE hShared; /* Shared memory segment used for locking */ | ||
101 | winceLock local; /* Locks obtained by this instance of winFile */ | ||
102 | winceLock *shared; /* Global shared lock memory for the file */ | ||
103 | #endif | ||
104 | }; | ||
105 | |||
106 | |||
107 | /* | ||
108 | ** The following variable is (normally) set once and never changes | ||
109 | ** thereafter. It records whether the operating system is Win95 | ||
110 | ** or WinNT. | ||
111 | ** | ||
112 | ** 0: Operating system unknown. | ||
113 | ** 1: Operating system is Win95. | ||
114 | ** 2: Operating system is WinNT. | ||
115 | ** | ||
116 | ** In order to facilitate testing on a WinNT system, the test fixture | ||
117 | ** can manually set this value to 1 to emulate Win98 behavior. | ||
118 | */ | ||
119 | #ifdef SQLITE_TEST | ||
120 | int sqlite3_os_type = 0; | ||
121 | #else | ||
122 | static int sqlite3_os_type = 0; | ||
123 | #endif | ||
124 | |||
125 | /* | ||
126 | ** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, | ||
127 | ** or WinCE. Return false (zero) for Win95, Win98, or WinME. | ||
128 | ** | ||
129 | ** Here is an interesting observation: Win95, Win98, and WinME lack | ||
130 | ** the LockFileEx() API. But we can still statically link against that | ||
131 | ** API as long as we don't call it win running Win95/98/ME. A call to | ||
132 | ** this routine is used to determine if the host is Win95/98/ME or | ||
133 | ** WinNT/2K/XP so that we will know whether or not we can safely call | ||
134 | ** the LockFileEx() API. | ||
135 | */ | ||
136 | #if OS_WINCE | ||
137 | # define isNT() (1) | ||
138 | #else | ||
139 | static int isNT(void){ | ||
140 | if( sqlite3_os_type==0 ){ | ||
141 | OSVERSIONINFO sInfo; | ||
142 | sInfo.dwOSVersionInfoSize = sizeof(sInfo); | ||
143 | GetVersionEx(&sInfo); | ||
144 | sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; | ||
145 | } | ||
146 | return sqlite3_os_type==2; | ||
147 | } | ||
148 | #endif /* OS_WINCE */ | ||
149 | |||
150 | /* | ||
151 | ** Convert a UTF-8 string to microsoft unicode (UTF-16?). | ||
152 | ** | ||
153 | ** Space to hold the returned string is obtained from malloc. | ||
154 | */ | ||
155 | static WCHAR *utf8ToUnicode(const char *zFilename){ | ||
156 | int nChar; | ||
157 | WCHAR *zWideFilename; | ||
158 | |||
159 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); | ||
160 | zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) ); | ||
161 | if( zWideFilename==0 ){ | ||
162 | return 0; | ||
163 | } | ||
164 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); | ||
165 | if( nChar==0 ){ | ||
166 | free(zWideFilename); | ||
167 | zWideFilename = 0; | ||
168 | } | ||
169 | return zWideFilename; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | ** Convert microsoft unicode to UTF-8. Space to hold the returned string is | ||
174 | ** obtained from malloc(). | ||
175 | */ | ||
176 | static char *unicodeToUtf8(const WCHAR *zWideFilename){ | ||
177 | int nByte; | ||
178 | char *zFilename; | ||
179 | |||
180 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); | ||
181 | zFilename = malloc( nByte ); | ||
182 | if( zFilename==0 ){ | ||
183 | return 0; | ||
184 | } | ||
185 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, | ||
186 | 0, 0); | ||
187 | if( nByte == 0 ){ | ||
188 | free(zFilename); | ||
189 | zFilename = 0; | ||
190 | } | ||
191 | return zFilename; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | ** Convert an ansi string to microsoft unicode, based on the | ||
196 | ** current codepage settings for file apis. | ||
197 | ** | ||
198 | ** Space to hold the returned string is obtained | ||
199 | ** from malloc. | ||
200 | */ | ||
201 | static WCHAR *mbcsToUnicode(const char *zFilename){ | ||
202 | int nByte; | ||
203 | WCHAR *zMbcsFilename; | ||
204 | int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | ||
205 | |||
206 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); | ||
207 | zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); | ||
208 | if( zMbcsFilename==0 ){ | ||
209 | return 0; | ||
210 | } | ||
211 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); | ||
212 | if( nByte==0 ){ | ||
213 | free(zMbcsFilename); | ||
214 | zMbcsFilename = 0; | ||
215 | } | ||
216 | return zMbcsFilename; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | ** Convert microsoft unicode to multibyte character string, based on the | ||
221 | ** user's Ansi codepage. | ||
222 | ** | ||
223 | ** Space to hold the returned string is obtained from | ||
224 | ** malloc(). | ||
225 | */ | ||
226 | static char *unicodeToMbcs(const WCHAR *zWideFilename){ | ||
227 | int nByte; | ||
228 | char *zFilename; | ||
229 | int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | ||
230 | |||
231 | nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); | ||
232 | zFilename = malloc( nByte ); | ||
233 | if( zFilename==0 ){ | ||
234 | return 0; | ||
235 | } | ||
236 | nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte, | ||
237 | 0, 0); | ||
238 | if( nByte == 0 ){ | ||
239 | free(zFilename); | ||
240 | zFilename = 0; | ||
241 | } | ||
242 | return zFilename; | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | ** Convert multibyte character string to UTF-8. Space to hold the | ||
247 | ** returned string is obtained from malloc(). | ||
248 | */ | ||
249 | static char *mbcsToUtf8(const char *zFilename){ | ||
250 | char *zFilenameUtf8; | ||
251 | WCHAR *zTmpWide; | ||
252 | |||
253 | zTmpWide = mbcsToUnicode(zFilename); | ||
254 | if( zTmpWide==0 ){ | ||
255 | return 0; | ||
256 | } | ||
257 | zFilenameUtf8 = unicodeToUtf8(zTmpWide); | ||
258 | free(zTmpWide); | ||
259 | return zFilenameUtf8; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | ** Convert UTF-8 to multibyte character string. Space to hold the | ||
264 | ** returned string is obtained from malloc(). | ||
265 | */ | ||
266 | static char *utf8ToMbcs(const char *zFilename){ | ||
267 | char *zFilenameMbcs; | ||
268 | WCHAR *zTmpWide; | ||
269 | |||
270 | zTmpWide = utf8ToUnicode(zFilename); | ||
271 | if( zTmpWide==0 ){ | ||
272 | return 0; | ||
273 | } | ||
274 | zFilenameMbcs = unicodeToMbcs(zTmpWide); | ||
275 | free(zTmpWide); | ||
276 | return zFilenameMbcs; | ||
277 | } | ||
278 | |||
279 | #if OS_WINCE | ||
280 | /************************************************************************* | ||
281 | ** This section contains code for WinCE only. | ||
282 | */ | ||
283 | /* | ||
284 | ** WindowsCE does not have a localtime() function. So create a | ||
285 | ** substitute. | ||
286 | */ | ||
287 | #include <time.h> | ||
288 | struct tm *__cdecl localtime(const time_t *t) | ||
289 | { | ||
290 | static struct tm y; | ||
291 | FILETIME uTm, lTm; | ||
292 | SYSTEMTIME pTm; | ||
293 | sqlite3_int64 t64; | ||
294 | t64 = *t; | ||
295 | t64 = (t64 + 11644473600)*10000000; | ||
296 | uTm.dwLowDateTime = t64 & 0xFFFFFFFF; | ||
297 | uTm.dwHighDateTime= t64 >> 32; | ||
298 | FileTimeToLocalFileTime(&uTm,&lTm); | ||
299 | FileTimeToSystemTime(&lTm,&pTm); | ||
300 | y.tm_year = pTm.wYear - 1900; | ||
301 | y.tm_mon = pTm.wMonth - 1; | ||
302 | y.tm_wday = pTm.wDayOfWeek; | ||
303 | y.tm_mday = pTm.wDay; | ||
304 | y.tm_hour = pTm.wHour; | ||
305 | y.tm_min = pTm.wMinute; | ||
306 | y.tm_sec = pTm.wSecond; | ||
307 | return &y; | ||
308 | } | ||
309 | |||
310 | /* This will never be called, but defined to make the code compile */ | ||
311 | #define GetTempPathA(a,b) | ||
312 | |||
313 | #define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e) | ||
314 | #define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) | ||
315 | #define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) | ||
316 | |||
317 | #define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-offsetof(winFile,h)] | ||
318 | |||
319 | /* | ||
320 | ** Acquire a lock on the handle h | ||
321 | */ | ||
322 | static void winceMutexAcquire(HANDLE h){ | ||
323 | DWORD dwErr; | ||
324 | do { | ||
325 | dwErr = WaitForSingleObject(h, INFINITE); | ||
326 | } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); | ||
327 | } | ||
328 | /* | ||
329 | ** Release a lock acquired by winceMutexAcquire() | ||
330 | */ | ||
331 | #define winceMutexRelease(h) ReleaseMutex(h) | ||
332 | |||
333 | /* | ||
334 | ** Create the mutex and shared memory used for locking in the file | ||
335 | ** descriptor pFile | ||
336 | */ | ||
337 | static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ | ||
338 | WCHAR *zTok; | ||
339 | WCHAR *zName = utf8ToUnicode(zFilename); | ||
340 | BOOL bInit = TRUE; | ||
341 | |||
342 | /* Initialize the local lockdata */ | ||
343 | ZeroMemory(&pFile->local, sizeof(pFile->local)); | ||
344 | |||
345 | /* Replace the backslashes from the filename and lowercase it | ||
346 | ** to derive a mutex name. */ | ||
347 | zTok = CharLowerW(zName); | ||
348 | for (;*zTok;zTok++){ | ||
349 | if (*zTok == '\\') *zTok = '_'; | ||
350 | } | ||
351 | |||
352 | /* Create/open the named mutex */ | ||
353 | pFile->hMutex = CreateMutexW(NULL, FALSE, zName); | ||
354 | if (!pFile->hMutex){ | ||
355 | free(zName); | ||
356 | return FALSE; | ||
357 | } | ||
358 | |||
359 | /* Acquire the mutex before continuing */ | ||
360 | winceMutexAcquire(pFile->hMutex); | ||
361 | |||
362 | /* Since the names of named mutexes, semaphores, file mappings etc are | ||
363 | ** case-sensitive, take advantage of that by uppercasing the mutex name | ||
364 | ** and using that as the shared filemapping name. | ||
365 | */ | ||
366 | CharUpperW(zName); | ||
367 | pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, | ||
368 | PAGE_READWRITE, 0, sizeof(winceLock), | ||
369 | zName); | ||
370 | |||
371 | /* Set a flag that indicates we're the first to create the memory so it | ||
372 | ** must be zero-initialized */ | ||
373 | if (GetLastError() == ERROR_ALREADY_EXISTS){ | ||
374 | bInit = FALSE; | ||
375 | } | ||
376 | |||
377 | free(zName); | ||
378 | |||
379 | /* If we succeeded in making the shared memory handle, map it. */ | ||
380 | if (pFile->hShared){ | ||
381 | pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared, | ||
382 | FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); | ||
383 | /* If mapping failed, close the shared memory handle and erase it */ | ||
384 | if (!pFile->shared){ | ||
385 | CloseHandle(pFile->hShared); | ||
386 | pFile->hShared = NULL; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | /* If shared memory could not be created, then close the mutex and fail */ | ||
391 | if (pFile->hShared == NULL){ | ||
392 | winceMutexRelease(pFile->hMutex); | ||
393 | CloseHandle(pFile->hMutex); | ||
394 | pFile->hMutex = NULL; | ||
395 | return FALSE; | ||
396 | } | ||
397 | |||
398 | /* Initialize the shared memory if we're supposed to */ | ||
399 | if (bInit) { | ||
400 | ZeroMemory(pFile->shared, sizeof(winceLock)); | ||
401 | } | ||
402 | |||
403 | winceMutexRelease(pFile->hMutex); | ||
404 | return TRUE; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | ** Destroy the part of winFile that deals with wince locks | ||
409 | */ | ||
410 | static void winceDestroyLock(winFile *pFile){ | ||
411 | if (pFile->hMutex){ | ||
412 | /* Acquire the mutex */ | ||
413 | winceMutexAcquire(pFile->hMutex); | ||
414 | |||
415 | /* The following blocks should probably assert in debug mode, but they | ||
416 | are to cleanup in case any locks remained open */ | ||
417 | if (pFile->local.nReaders){ | ||
418 | pFile->shared->nReaders --; | ||
419 | } | ||
420 | if (pFile->local.bReserved){ | ||
421 | pFile->shared->bReserved = FALSE; | ||
422 | } | ||
423 | if (pFile->local.bPending){ | ||
424 | pFile->shared->bPending = FALSE; | ||
425 | } | ||
426 | if (pFile->local.bExclusive){ | ||
427 | pFile->shared->bExclusive = FALSE; | ||
428 | } | ||
429 | |||
430 | /* De-reference and close our copy of the shared memory handle */ | ||
431 | UnmapViewOfFile(pFile->shared); | ||
432 | CloseHandle(pFile->hShared); | ||
433 | |||
434 | if( pFile->zDeleteOnClose ){ | ||
435 | DeleteFileW(pFile->zDeleteOnClose); | ||
436 | free(pFile->zDeleteOnClose); | ||
437 | pFile->zDeleteOnClose = 0; | ||
438 | } | ||
439 | |||
440 | /* Done with the mutex */ | ||
441 | winceMutexRelease(pFile->hMutex); | ||
442 | CloseHandle(pFile->hMutex); | ||
443 | pFile->hMutex = NULL; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | ** An implementation of the LockFile() API of windows for wince | ||
449 | */ | ||
450 | static BOOL winceLockFile( | ||
451 | HANDLE *phFile, | ||
452 | DWORD dwFileOffsetLow, | ||
453 | DWORD dwFileOffsetHigh, | ||
454 | DWORD nNumberOfBytesToLockLow, | ||
455 | DWORD nNumberOfBytesToLockHigh | ||
456 | ){ | ||
457 | winFile *pFile = HANDLE_TO_WINFILE(phFile); | ||
458 | BOOL bReturn = FALSE; | ||
459 | |||
460 | if (!pFile->hMutex) return TRUE; | ||
461 | winceMutexAcquire(pFile->hMutex); | ||
462 | |||
463 | /* Wanting an exclusive lock? */ | ||
464 | if (dwFileOffsetLow == SHARED_FIRST | ||
465 | && nNumberOfBytesToLockLow == SHARED_SIZE){ | ||
466 | if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ | ||
467 | pFile->shared->bExclusive = TRUE; | ||
468 | pFile->local.bExclusive = TRUE; | ||
469 | bReturn = TRUE; | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /* Want a read-only lock? */ | ||
474 | else if ((dwFileOffsetLow >= SHARED_FIRST && | ||
475 | dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE) && | ||
476 | nNumberOfBytesToLockLow == 1){ | ||
477 | if (pFile->shared->bExclusive == 0){ | ||
478 | pFile->local.nReaders ++; | ||
479 | if (pFile->local.nReaders == 1){ | ||
480 | pFile->shared->nReaders ++; | ||
481 | } | ||
482 | bReturn = TRUE; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /* Want a pending lock? */ | ||
487 | else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToLockLow == 1){ | ||
488 | /* If no pending lock has been acquired, then acquire it */ | ||
489 | if (pFile->shared->bPending == 0) { | ||
490 | pFile->shared->bPending = TRUE; | ||
491 | pFile->local.bPending = TRUE; | ||
492 | bReturn = TRUE; | ||
493 | } | ||
494 | } | ||
495 | /* Want a reserved lock? */ | ||
496 | else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ | ||
497 | if (pFile->shared->bReserved == 0) { | ||
498 | pFile->shared->bReserved = TRUE; | ||
499 | pFile->local.bReserved = TRUE; | ||
500 | bReturn = TRUE; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | winceMutexRelease(pFile->hMutex); | ||
505 | return bReturn; | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | ** An implementation of the UnlockFile API of windows for wince | ||
510 | */ | ||
511 | static BOOL winceUnlockFile( | ||
512 | HANDLE *phFile, | ||
513 | DWORD dwFileOffsetLow, | ||
514 | DWORD dwFileOffsetHigh, | ||
515 | DWORD nNumberOfBytesToUnlockLow, | ||
516 | DWORD nNumberOfBytesToUnlockHigh | ||
517 | ){ | ||
518 | winFile *pFile = HANDLE_TO_WINFILE(phFile); | ||
519 | BOOL bReturn = FALSE; | ||
520 | |||
521 | if (!pFile->hMutex) return TRUE; | ||
522 | winceMutexAcquire(pFile->hMutex); | ||
523 | |||
524 | /* Releasing a reader lock or an exclusive lock */ | ||
525 | if (dwFileOffsetLow >= SHARED_FIRST && | ||
526 | dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE){ | ||
527 | /* Did we have an exclusive lock? */ | ||
528 | if (pFile->local.bExclusive){ | ||
529 | pFile->local.bExclusive = FALSE; | ||
530 | pFile->shared->bExclusive = FALSE; | ||
531 | bReturn = TRUE; | ||
532 | } | ||
533 | |||
534 | /* Did we just have a reader lock? */ | ||
535 | else if (pFile->local.nReaders){ | ||
536 | pFile->local.nReaders --; | ||
537 | if (pFile->local.nReaders == 0) | ||
538 | { | ||
539 | pFile->shared->nReaders --; | ||
540 | } | ||
541 | bReturn = TRUE; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | /* Releasing a pending lock */ | ||
546 | else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ | ||
547 | if (pFile->local.bPending){ | ||
548 | pFile->local.bPending = FALSE; | ||
549 | pFile->shared->bPending = FALSE; | ||
550 | bReturn = TRUE; | ||
551 | } | ||
552 | } | ||
553 | /* Releasing a reserved lock */ | ||
554 | else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ | ||
555 | if (pFile->local.bReserved) { | ||
556 | pFile->local.bReserved = FALSE; | ||
557 | pFile->shared->bReserved = FALSE; | ||
558 | bReturn = TRUE; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | winceMutexRelease(pFile->hMutex); | ||
563 | return bReturn; | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | ** An implementation of the LockFileEx() API of windows for wince | ||
568 | */ | ||
569 | static BOOL winceLockFileEx( | ||
570 | HANDLE *phFile, | ||
571 | DWORD dwFlags, | ||
572 | DWORD dwReserved, | ||
573 | DWORD nNumberOfBytesToLockLow, | ||
574 | DWORD nNumberOfBytesToLockHigh, | ||
575 | LPOVERLAPPED lpOverlapped | ||
576 | ){ | ||
577 | /* If the caller wants a shared read lock, forward this call | ||
578 | ** to winceLockFile */ | ||
579 | if (lpOverlapped->Offset == SHARED_FIRST && | ||
580 | dwFlags == 1 && | ||
581 | nNumberOfBytesToLockLow == SHARED_SIZE){ | ||
582 | return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0); | ||
583 | } | ||
584 | return FALSE; | ||
585 | } | ||
586 | /* | ||
587 | ** End of the special code for wince | ||
588 | *****************************************************************************/ | ||
589 | #endif /* OS_WINCE */ | ||
590 | |||
591 | /***************************************************************************** | ||
592 | ** The next group of routines implement the I/O methods specified | ||
593 | ** by the sqlite3_io_methods object. | ||
594 | ******************************************************************************/ | ||
595 | |||
596 | /* | ||
597 | ** Close a file. | ||
598 | ** | ||
599 | ** It is reported that an attempt to close a handle might sometimes | ||
600 | ** fail. This is a very unreasonable result, but windows is notorious | ||
601 | ** for being unreasonable so I do not doubt that it might happen. If | ||
602 | ** the close fails, we pause for 100 milliseconds and try again. As | ||
603 | ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before | ||
604 | ** giving up and returning an error. | ||
605 | */ | ||
606 | #define MX_CLOSE_ATTEMPT 3 | ||
607 | static int winClose(sqlite3_file *id){ | ||
608 | int rc, cnt = 0; | ||
609 | winFile *pFile = (winFile*)id; | ||
610 | OSTRACE2("CLOSE %d\n", pFile->h); | ||
611 | do{ | ||
612 | rc = CloseHandle(pFile->h); | ||
613 | }while( rc==0 && cnt++ < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); | ||
614 | #if OS_WINCE | ||
615 | winceDestroyLock(pFile); | ||
616 | #endif | ||
617 | OpenCounter(-1); | ||
618 | return rc ? SQLITE_OK : SQLITE_IOERR; | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | ** Some microsoft compilers lack this definition. | ||
623 | */ | ||
624 | #ifndef INVALID_SET_FILE_POINTER | ||
625 | # define INVALID_SET_FILE_POINTER ((DWORD)-1) | ||
626 | #endif | ||
627 | |||
628 | /* | ||
629 | ** Read data from a file into a buffer. Return SQLITE_OK if all | ||
630 | ** bytes were read successfully and SQLITE_IOERR if anything goes | ||
631 | ** wrong. | ||
632 | */ | ||
633 | static int winRead( | ||
634 | sqlite3_file *id, /* File to read from */ | ||
635 | void *pBuf, /* Write content into this buffer */ | ||
636 | int amt, /* Number of bytes to read */ | ||
637 | sqlite3_int64 offset /* Begin reading at this offset */ | ||
638 | ){ | ||
639 | LONG upperBits = (offset>>32) & 0x7fffffff; | ||
640 | LONG lowerBits = offset & 0xffffffff; | ||
641 | DWORD rc; | ||
642 | DWORD got; | ||
643 | winFile *pFile = (winFile*)id; | ||
644 | assert( id!=0 ); | ||
645 | SimulateIOError(return SQLITE_IOERR_READ); | ||
646 | OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype); | ||
647 | rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); | ||
648 | if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){ | ||
649 | return SQLITE_FULL; | ||
650 | } | ||
651 | if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){ | ||
652 | return SQLITE_IOERR_READ; | ||
653 | } | ||
654 | if( got==(DWORD)amt ){ | ||
655 | return SQLITE_OK; | ||
656 | }else{ | ||
657 | memset(&((char*)pBuf)[got], 0, amt-got); | ||
658 | return SQLITE_IOERR_SHORT_READ; | ||
659 | } | ||
660 | } | ||
661 | |||
662 | /* | ||
663 | ** Write data from a buffer into a file. Return SQLITE_OK on success | ||
664 | ** or some other error code on failure. | ||
665 | */ | ||
666 | static int winWrite( | ||
667 | sqlite3_file *id, /* File to write into */ | ||
668 | const void *pBuf, /* The bytes to be written */ | ||
669 | int amt, /* Number of bytes to write */ | ||
670 | sqlite3_int64 offset /* Offset into the file to begin writing at */ | ||
671 | ){ | ||
672 | LONG upperBits = (offset>>32) & 0x7fffffff; | ||
673 | LONG lowerBits = offset & 0xffffffff; | ||
674 | DWORD rc; | ||
675 | DWORD wrote; | ||
676 | winFile *pFile = (winFile*)id; | ||
677 | assert( id!=0 ); | ||
678 | SimulateIOError(return SQLITE_IOERR_WRITE); | ||
679 | SimulateDiskfullError(return SQLITE_FULL); | ||
680 | OSTRACE3("WRITE %d lock=%d\n", pFile->h, pFile->locktype); | ||
681 | rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); | ||
682 | if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){ | ||
683 | return SQLITE_FULL; | ||
684 | } | ||
685 | assert( amt>0 ); | ||
686 | while( | ||
687 | amt>0 | ||
688 | && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0 | ||
689 | && wrote>0 | ||
690 | ){ | ||
691 | amt -= wrote; | ||
692 | pBuf = &((char*)pBuf)[wrote]; | ||
693 | } | ||
694 | if( !rc || amt>(int)wrote ){ | ||
695 | return SQLITE_FULL; | ||
696 | } | ||
697 | return SQLITE_OK; | ||
698 | } | ||
699 | |||
700 | /* | ||
701 | ** Truncate an open file to a specified size | ||
702 | */ | ||
703 | static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ | ||
704 | LONG upperBits = (nByte>>32) & 0x7fffffff; | ||
705 | LONG lowerBits = nByte & 0xffffffff; | ||
706 | winFile *pFile = (winFile*)id; | ||
707 | OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte); | ||
708 | SimulateIOError(return SQLITE_IOERR_TRUNCATE); | ||
709 | SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); | ||
710 | SetEndOfFile(pFile->h); | ||
711 | return SQLITE_OK; | ||
712 | } | ||
713 | |||
714 | #ifdef SQLITE_TEST | ||
715 | /* | ||
716 | ** Count the number of fullsyncs and normal syncs. This is used to test | ||
717 | ** that syncs and fullsyncs are occuring at the right times. | ||
718 | */ | ||
719 | int sqlite3_sync_count = 0; | ||
720 | int sqlite3_fullsync_count = 0; | ||
721 | #endif | ||
722 | |||
723 | /* | ||
724 | ** Make sure all writes to a particular file are committed to disk. | ||
725 | */ | ||
726 | static int winSync(sqlite3_file *id, int flags){ | ||
727 | winFile *pFile = (winFile*)id; | ||
728 | OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype); | ||
729 | #ifdef SQLITE_TEST | ||
730 | if( flags & SQLITE_SYNC_FULL ){ | ||
731 | sqlite3_fullsync_count++; | ||
732 | } | ||
733 | sqlite3_sync_count++; | ||
734 | #endif | ||
735 | if( FlushFileBuffers(pFile->h) ){ | ||
736 | return SQLITE_OK; | ||
737 | }else{ | ||
738 | return SQLITE_IOERR; | ||
739 | } | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | ** Determine the current size of a file in bytes | ||
744 | */ | ||
745 | static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ | ||
746 | winFile *pFile = (winFile*)id; | ||
747 | DWORD upperBits, lowerBits; | ||
748 | SimulateIOError(return SQLITE_IOERR_FSTAT); | ||
749 | lowerBits = GetFileSize(pFile->h, &upperBits); | ||
750 | *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; | ||
751 | return SQLITE_OK; | ||
752 | } | ||
753 | |||
754 | /* | ||
755 | ** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. | ||
756 | */ | ||
757 | #ifndef LOCKFILE_FAIL_IMMEDIATELY | ||
758 | # define LOCKFILE_FAIL_IMMEDIATELY 1 | ||
759 | #endif | ||
760 | |||
761 | /* | ||
762 | ** Acquire a reader lock. | ||
763 | ** Different API routines are called depending on whether or not this | ||
764 | ** is Win95 or WinNT. | ||
765 | */ | ||
766 | static int getReadLock(winFile *pFile){ | ||
767 | int res; | ||
768 | if( isNT() ){ | ||
769 | OVERLAPPED ovlp; | ||
770 | ovlp.Offset = SHARED_FIRST; | ||
771 | ovlp.OffsetHigh = 0; | ||
772 | ovlp.hEvent = 0; | ||
773 | res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY, | ||
774 | 0, SHARED_SIZE, 0, &ovlp); | ||
775 | }else{ | ||
776 | int lk; | ||
777 | sqlite3Randomness(sizeof(lk), &lk); | ||
778 | pFile->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); | ||
779 | res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); | ||
780 | } | ||
781 | return res; | ||
782 | } | ||
783 | |||
784 | /* | ||
785 | ** Undo a readlock | ||
786 | */ | ||
787 | static int unlockReadLock(winFile *pFile){ | ||
788 | int res; | ||
789 | if( isNT() ){ | ||
790 | res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
791 | }else{ | ||
792 | res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); | ||
793 | } | ||
794 | return res; | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | ** Lock the file with the lock specified by parameter locktype - one | ||
799 | ** of the following: | ||
800 | ** | ||
801 | ** (1) SHARED_LOCK | ||
802 | ** (2) RESERVED_LOCK | ||
803 | ** (3) PENDING_LOCK | ||
804 | ** (4) EXCLUSIVE_LOCK | ||
805 | ** | ||
806 | ** Sometimes when requesting one lock state, additional lock states | ||
807 | ** are inserted in between. The locking might fail on one of the later | ||
808 | ** transitions leaving the lock state different from what it started but | ||
809 | ** still short of its goal. The following chart shows the allowed | ||
810 | ** transitions and the inserted intermediate states: | ||
811 | ** | ||
812 | ** UNLOCKED -> SHARED | ||
813 | ** SHARED -> RESERVED | ||
814 | ** SHARED -> (PENDING) -> EXCLUSIVE | ||
815 | ** RESERVED -> (PENDING) -> EXCLUSIVE | ||
816 | ** PENDING -> EXCLUSIVE | ||
817 | ** | ||
818 | ** This routine will only increase a lock. The winUnlock() routine | ||
819 | ** erases all locks at once and returns us immediately to locking level 0. | ||
820 | ** It is not possible to lower the locking level one step at a time. You | ||
821 | ** must go straight to locking level 0. | ||
822 | */ | ||
823 | static int winLock(sqlite3_file *id, int locktype){ | ||
824 | int rc = SQLITE_OK; /* Return code from subroutines */ | ||
825 | int res = 1; /* Result of a windows lock call */ | ||
826 | int newLocktype; /* Set pFile->locktype to this value before exiting */ | ||
827 | int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ | ||
828 | winFile *pFile = (winFile*)id; | ||
829 | |||
830 | assert( pFile!=0 ); | ||
831 | OSTRACE5("LOCK %d %d was %d(%d)\n", | ||
832 | pFile->h, locktype, pFile->locktype, pFile->sharedLockByte); | ||
833 | |||
834 | /* If there is already a lock of this type or more restrictive on the | ||
835 | ** OsFile, do nothing. Don't use the end_lock: exit path, as | ||
836 | ** sqlite3OsEnterMutex() hasn't been called yet. | ||
837 | */ | ||
838 | if( pFile->locktype>=locktype ){ | ||
839 | return SQLITE_OK; | ||
840 | } | ||
841 | |||
842 | /* Make sure the locking sequence is correct | ||
843 | */ | ||
844 | assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); | ||
845 | assert( locktype!=PENDING_LOCK ); | ||
846 | assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); | ||
847 | |||
848 | /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or | ||
849 | ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of | ||
850 | ** the PENDING_LOCK byte is temporary. | ||
851 | */ | ||
852 | newLocktype = pFile->locktype; | ||
853 | if( pFile->locktype==NO_LOCK | ||
854 | || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) | ||
855 | ){ | ||
856 | int cnt = 3; | ||
857 | while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ | ||
858 | /* Try 3 times to get the pending lock. The pending lock might be | ||
859 | ** held by another reader process who will release it momentarily. | ||
860 | */ | ||
861 | OSTRACE2("could not get a PENDING lock. cnt=%d\n", cnt); | ||
862 | Sleep(1); | ||
863 | } | ||
864 | gotPendingLock = res; | ||
865 | } | ||
866 | |||
867 | /* Acquire a shared lock | ||
868 | */ | ||
869 | if( locktype==SHARED_LOCK && res ){ | ||
870 | assert( pFile->locktype==NO_LOCK ); | ||
871 | res = getReadLock(pFile); | ||
872 | if( res ){ | ||
873 | newLocktype = SHARED_LOCK; | ||
874 | } | ||
875 | } | ||
876 | |||
877 | /* Acquire a RESERVED lock | ||
878 | */ | ||
879 | if( locktype==RESERVED_LOCK && res ){ | ||
880 | assert( pFile->locktype==SHARED_LOCK ); | ||
881 | res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
882 | if( res ){ | ||
883 | newLocktype = RESERVED_LOCK; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | /* Acquire a PENDING lock | ||
888 | */ | ||
889 | if( locktype==EXCLUSIVE_LOCK && res ){ | ||
890 | newLocktype = PENDING_LOCK; | ||
891 | gotPendingLock = 0; | ||
892 | } | ||
893 | |||
894 | /* Acquire an EXCLUSIVE lock | ||
895 | */ | ||
896 | if( locktype==EXCLUSIVE_LOCK && res ){ | ||
897 | assert( pFile->locktype>=SHARED_LOCK ); | ||
898 | res = unlockReadLock(pFile); | ||
899 | OSTRACE2("unreadlock = %d\n", res); | ||
900 | res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
901 | if( res ){ | ||
902 | newLocktype = EXCLUSIVE_LOCK; | ||
903 | }else{ | ||
904 | OSTRACE2("error-code = %d\n", GetLastError()); | ||
905 | getReadLock(pFile); | ||
906 | } | ||
907 | } | ||
908 | |||
909 | /* If we are holding a PENDING lock that ought to be released, then | ||
910 | ** release it now. | ||
911 | */ | ||
912 | if( gotPendingLock && locktype==SHARED_LOCK ){ | ||
913 | UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); | ||
914 | } | ||
915 | |||
916 | /* Update the state of the lock has held in the file descriptor then | ||
917 | ** return the appropriate result code. | ||
918 | */ | ||
919 | if( res ){ | ||
920 | rc = SQLITE_OK; | ||
921 | }else{ | ||
922 | OSTRACE4("LOCK FAILED %d trying for %d but got %d\n", pFile->h, | ||
923 | locktype, newLocktype); | ||
924 | rc = SQLITE_BUSY; | ||
925 | } | ||
926 | pFile->locktype = newLocktype; | ||
927 | return rc; | ||
928 | } | ||
929 | |||
930 | /* | ||
931 | ** This routine checks if there is a RESERVED lock held on the specified | ||
932 | ** file by this or any other process. If such a lock is held, return | ||
933 | ** non-zero, otherwise zero. | ||
934 | */ | ||
935 | static int winCheckReservedLock(sqlite3_file *id){ | ||
936 | int rc; | ||
937 | winFile *pFile = (winFile*)id; | ||
938 | assert( pFile!=0 ); | ||
939 | if( pFile->locktype>=RESERVED_LOCK ){ | ||
940 | rc = 1; | ||
941 | OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc); | ||
942 | }else{ | ||
943 | rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
944 | if( rc ){ | ||
945 | UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
946 | } | ||
947 | rc = !rc; | ||
948 | OSTRACE3("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc); | ||
949 | } | ||
950 | return rc; | ||
951 | } | ||
952 | |||
953 | /* | ||
954 | ** Lower the locking level on file descriptor id to locktype. locktype | ||
955 | ** must be either NO_LOCK or SHARED_LOCK. | ||
956 | ** | ||
957 | ** If the locking level of the file descriptor is already at or below | ||
958 | ** the requested locking level, this routine is a no-op. | ||
959 | ** | ||
960 | ** It is not possible for this routine to fail if the second argument | ||
961 | ** is NO_LOCK. If the second argument is SHARED_LOCK then this routine | ||
962 | ** might return SQLITE_IOERR; | ||
963 | */ | ||
964 | static int winUnlock(sqlite3_file *id, int locktype){ | ||
965 | int type; | ||
966 | winFile *pFile = (winFile*)id; | ||
967 | int rc = SQLITE_OK; | ||
968 | assert( pFile!=0 ); | ||
969 | assert( locktype<=SHARED_LOCK ); | ||
970 | OSTRACE5("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, | ||
971 | pFile->locktype, pFile->sharedLockByte); | ||
972 | type = pFile->locktype; | ||
973 | if( type>=EXCLUSIVE_LOCK ){ | ||
974 | UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
975 | if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ | ||
976 | /* This should never happen. We should always be able to | ||
977 | ** reacquire the read lock */ | ||
978 | rc = SQLITE_IOERR_UNLOCK; | ||
979 | } | ||
980 | } | ||
981 | if( type>=RESERVED_LOCK ){ | ||
982 | UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
983 | } | ||
984 | if( locktype==NO_LOCK && type>=SHARED_LOCK ){ | ||
985 | unlockReadLock(pFile); | ||
986 | } | ||
987 | if( type>=PENDING_LOCK ){ | ||
988 | UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); | ||
989 | } | ||
990 | pFile->locktype = locktype; | ||
991 | return rc; | ||
992 | } | ||
993 | |||
994 | /* | ||
995 | ** Control and query of the open file handle. | ||
996 | */ | ||
997 | static int winFileControl(sqlite3_file *id, int op, void *pArg){ | ||
998 | switch( op ){ | ||
999 | case SQLITE_FCNTL_LOCKSTATE: { | ||
1000 | *(int*)pArg = ((winFile*)id)->locktype; | ||
1001 | return SQLITE_OK; | ||
1002 | } | ||
1003 | } | ||
1004 | return SQLITE_ERROR; | ||
1005 | } | ||
1006 | |||
1007 | /* | ||
1008 | ** Return the sector size in bytes of the underlying block device for | ||
1009 | ** the specified file. This is almost always 512 bytes, but may be | ||
1010 | ** larger for some devices. | ||
1011 | ** | ||
1012 | ** SQLite code assumes this function cannot fail. It also assumes that | ||
1013 | ** if two files are created in the same file-system directory (i.e. | ||
1014 | ** a database and it's journal file) that the sector size will be the | ||
1015 | ** same for both. | ||
1016 | */ | ||
1017 | static int winSectorSize(sqlite3_file *id){ | ||
1018 | return SQLITE_DEFAULT_SECTOR_SIZE; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | ** Return a vector of device characteristics. | ||
1023 | */ | ||
1024 | static int winDeviceCharacteristics(sqlite3_file *id){ | ||
1025 | return 0; | ||
1026 | } | ||
1027 | |||
1028 | /* | ||
1029 | ** This vector defines all the methods that can operate on an | ||
1030 | ** sqlite3_file for win32. | ||
1031 | */ | ||
1032 | static const sqlite3_io_methods winIoMethod = { | ||
1033 | 1, /* iVersion */ | ||
1034 | winClose, | ||
1035 | winRead, | ||
1036 | winWrite, | ||
1037 | winTruncate, | ||
1038 | winSync, | ||
1039 | winFileSize, | ||
1040 | winLock, | ||
1041 | winUnlock, | ||
1042 | winCheckReservedLock, | ||
1043 | winFileControl, | ||
1044 | winSectorSize, | ||
1045 | winDeviceCharacteristics | ||
1046 | }; | ||
1047 | |||
1048 | /*************************************************************************** | ||
1049 | ** Here ends the I/O methods that form the sqlite3_io_methods object. | ||
1050 | ** | ||
1051 | ** The next block of code implements the VFS methods. | ||
1052 | ****************************************************************************/ | ||
1053 | |||
1054 | /* | ||
1055 | ** Convert a UTF-8 filename into whatever form the underlying | ||
1056 | ** operating system wants filenames in. Space to hold the result | ||
1057 | ** is obtained from malloc and must be freed by the calling | ||
1058 | ** function. | ||
1059 | */ | ||
1060 | static void *convertUtf8Filename(const char *zFilename){ | ||
1061 | void *zConverted = 0; | ||
1062 | if( isNT() ){ | ||
1063 | zConverted = utf8ToUnicode(zFilename); | ||
1064 | }else{ | ||
1065 | zConverted = utf8ToMbcs(zFilename); | ||
1066 | } | ||
1067 | /* caller will handle out of memory */ | ||
1068 | return zConverted; | ||
1069 | } | ||
1070 | |||
1071 | /* | ||
1072 | ** Open a file. | ||
1073 | */ | ||
1074 | static int winOpen( | ||
1075 | sqlite3_vfs *pVfs, /* Not used */ | ||
1076 | const char *zName, /* Name of the file (UTF-8) */ | ||
1077 | sqlite3_file *id, /* Write the SQLite file handle here */ | ||
1078 | int flags, /* Open mode flags */ | ||
1079 | int *pOutFlags /* Status return flags */ | ||
1080 | ){ | ||
1081 | HANDLE h; | ||
1082 | DWORD dwDesiredAccess; | ||
1083 | DWORD dwShareMode; | ||
1084 | DWORD dwCreationDisposition; | ||
1085 | DWORD dwFlagsAndAttributes = 0; | ||
1086 | winFile *pFile = (winFile*)id; | ||
1087 | void *zConverted = convertUtf8Filename(zName); | ||
1088 | if( zConverted==0 ){ | ||
1089 | return SQLITE_NOMEM; | ||
1090 | } | ||
1091 | |||
1092 | if( flags & SQLITE_OPEN_READWRITE ){ | ||
1093 | dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; | ||
1094 | }else{ | ||
1095 | dwDesiredAccess = GENERIC_READ; | ||
1096 | } | ||
1097 | if( flags & SQLITE_OPEN_CREATE ){ | ||
1098 | dwCreationDisposition = OPEN_ALWAYS; | ||
1099 | }else{ | ||
1100 | dwCreationDisposition = OPEN_EXISTING; | ||
1101 | } | ||
1102 | if( flags & SQLITE_OPEN_MAIN_DB ){ | ||
1103 | dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | ||
1104 | }else{ | ||
1105 | dwShareMode = 0; | ||
1106 | } | ||
1107 | if( flags & (SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TEMP_JOURNAL | ||
1108 | | SQLITE_OPEN_SUBJOURNAL) ){ | ||
1109 | dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | ||
1110 | | FILE_ATTRIBUTE_HIDDEN | ||
1111 | | FILE_FLAG_DELETE_ON_CLOSE; | ||
1112 | }else{ | ||
1113 | dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; | ||
1114 | } | ||
1115 | if( flags & (SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_TEMP_DB) ){ | ||
1116 | dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; | ||
1117 | }else{ | ||
1118 | dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN; | ||
1119 | } | ||
1120 | if( isNT() ){ | ||
1121 | h = CreateFileW((WCHAR*)zConverted, | ||
1122 | dwDesiredAccess, | ||
1123 | dwShareMode, | ||
1124 | NULL, | ||
1125 | dwCreationDisposition, | ||
1126 | dwFlagsAndAttributes, | ||
1127 | NULL | ||
1128 | ); | ||
1129 | }else{ | ||
1130 | #if OS_WINCE | ||
1131 | return SQLITE_NOMEM; | ||
1132 | #else | ||
1133 | h = CreateFileA((char*)zConverted, | ||
1134 | dwDesiredAccess, | ||
1135 | dwShareMode, | ||
1136 | NULL, | ||
1137 | dwCreationDisposition, | ||
1138 | dwFlagsAndAttributes, | ||
1139 | NULL | ||
1140 | ); | ||
1141 | #endif | ||
1142 | } | ||
1143 | if( h==INVALID_HANDLE_VALUE ){ | ||
1144 | free(zConverted); | ||
1145 | if( flags & SQLITE_OPEN_READWRITE ){ | ||
1146 | return winOpen(0, zName, id, | ||
1147 | ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); | ||
1148 | }else{ | ||
1149 | return SQLITE_CANTOPEN; | ||
1150 | } | ||
1151 | } | ||
1152 | if( pOutFlags ){ | ||
1153 | if( flags & SQLITE_OPEN_READWRITE ){ | ||
1154 | *pOutFlags = SQLITE_OPEN_READWRITE; | ||
1155 | }else{ | ||
1156 | *pOutFlags = SQLITE_OPEN_READONLY; | ||
1157 | } | ||
1158 | } | ||
1159 | memset(pFile, 0, sizeof(*pFile)); | ||
1160 | pFile->pMethod = &winIoMethod; | ||
1161 | pFile->h = h; | ||
1162 | #if OS_WINCE | ||
1163 | if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == | ||
1164 | (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) | ||
1165 | && !winceCreateLock(zFilename, pFile) | ||
1166 | ){ | ||
1167 | CloseHandle(h); | ||
1168 | free(zConverted); | ||
1169 | return SQLITE_CANTOPEN; | ||
1170 | } | ||
1171 | if( dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE ){ | ||
1172 | pFile->zDeleteOnClose = zConverted; | ||
1173 | }else | ||
1174 | #endif | ||
1175 | { | ||
1176 | free(zConverted); | ||
1177 | } | ||
1178 | OpenCounter(+1); | ||
1179 | return SQLITE_OK; | ||
1180 | } | ||
1181 | |||
1182 | /* | ||
1183 | ** Delete the named file. | ||
1184 | ** | ||
1185 | ** Note that windows does not allow a file to be deleted if some other | ||
1186 | ** process has it open. Sometimes a virus scanner or indexing program | ||
1187 | ** will open a journal file shortly after it is created in order to do | ||
1188 | ** whatever it is it does. While this other process is holding the | ||
1189 | ** file open, we will be unable to delete it. To work around this | ||
1190 | ** problem, we delay 100 milliseconds and try to delete again. Up | ||
1191 | ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving | ||
1192 | ** up and returning an error. | ||
1193 | */ | ||
1194 | #define MX_DELETION_ATTEMPTS 3 | ||
1195 | static int winDelete( | ||
1196 | sqlite3_vfs *pVfs, /* Not used on win32 */ | ||
1197 | const char *zFilename, /* Name of file to delete */ | ||
1198 | int syncDir /* Not used on win32 */ | ||
1199 | ){ | ||
1200 | int cnt = 0; | ||
1201 | int rc; | ||
1202 | void *zConverted = convertUtf8Filename(zFilename); | ||
1203 | if( zConverted==0 ){ | ||
1204 | return SQLITE_NOMEM; | ||
1205 | } | ||
1206 | SimulateIOError(return SQLITE_IOERR_DELETE); | ||
1207 | if( isNT() ){ | ||
1208 | do{ | ||
1209 | rc = DeleteFileW(zConverted); | ||
1210 | }while( rc==0 && GetFileAttributesW(zConverted)!=0xffffffff | ||
1211 | && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) ); | ||
1212 | }else{ | ||
1213 | #if OS_WINCE | ||
1214 | return SQLITE_NOMEM; | ||
1215 | #else | ||
1216 | do{ | ||
1217 | rc = DeleteFileA(zConverted); | ||
1218 | }while( rc==0 && GetFileAttributesA(zConverted)!=0xffffffff | ||
1219 | && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) ); | ||
1220 | #endif | ||
1221 | } | ||
1222 | free(zConverted); | ||
1223 | OSTRACE2("DELETE \"%s\"\n", zFilename); | ||
1224 | return rc!=0 ? SQLITE_OK : SQLITE_IOERR; | ||
1225 | } | ||
1226 | |||
1227 | /* | ||
1228 | ** Check the existance and status of a file. | ||
1229 | */ | ||
1230 | static int winAccess( | ||
1231 | sqlite3_vfs *pVfs, /* Not used on win32 */ | ||
1232 | const char *zFilename, /* Name of file to check */ | ||
1233 | int flags /* Type of test to make on this file */ | ||
1234 | ){ | ||
1235 | DWORD attr; | ||
1236 | int rc; | ||
1237 | void *zConverted = convertUtf8Filename(zFilename); | ||
1238 | if( zConverted==0 ){ | ||
1239 | return SQLITE_NOMEM; | ||
1240 | } | ||
1241 | if( isNT() ){ | ||
1242 | attr = GetFileAttributesW((WCHAR*)zConverted); | ||
1243 | }else{ | ||
1244 | #if OS_WINCE | ||
1245 | return SQLITE_NOMEM; | ||
1246 | #else | ||
1247 | attr = GetFileAttributesA((char*)zConverted); | ||
1248 | #endif | ||
1249 | } | ||
1250 | free(zConverted); | ||
1251 | switch( flags ){ | ||
1252 | case SQLITE_ACCESS_READ: | ||
1253 | case SQLITE_ACCESS_EXISTS: | ||
1254 | rc = attr!=0xffffffff; | ||
1255 | break; | ||
1256 | case SQLITE_ACCESS_READWRITE: | ||
1257 | rc = (attr & FILE_ATTRIBUTE_READONLY)==0; | ||
1258 | break; | ||
1259 | default: | ||
1260 | assert(!"Invalid flags argument"); | ||
1261 | } | ||
1262 | return rc; | ||
1263 | } | ||
1264 | |||
1265 | |||
1266 | /* | ||
1267 | ** Create a temporary file name in zBuf. zBuf must be big enough to | ||
1268 | ** hold at pVfs->mxPathname characters. | ||
1269 | */ | ||
1270 | static int winGetTempname(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ | ||
1271 | static char zChars[] = | ||
1272 | "abcdefghijklmnopqrstuvwxyz" | ||
1273 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
1274 | "0123456789"; | ||
1275 | int i, j; | ||
1276 | char zTempPath[MAX_PATH+1]; | ||
1277 | if( sqlite3_temp_directory ){ | ||
1278 | sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); | ||
1279 | }else if( isNT() ){ | ||
1280 | char *zMulti; | ||
1281 | WCHAR zWidePath[MAX_PATH]; | ||
1282 | GetTempPathW(MAX_PATH-30, zWidePath); | ||
1283 | zMulti = unicodeToUtf8(zWidePath); | ||
1284 | if( zMulti ){ | ||
1285 | sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); | ||
1286 | free(zMulti); | ||
1287 | }else{ | ||
1288 | return SQLITE_NOMEM; | ||
1289 | } | ||
1290 | }else{ | ||
1291 | char *zUtf8; | ||
1292 | char zMbcsPath[MAX_PATH]; | ||
1293 | GetTempPathA(MAX_PATH-30, zMbcsPath); | ||
1294 | zUtf8 = mbcsToUtf8(zMbcsPath); | ||
1295 | if( zUtf8 ){ | ||
1296 | sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); | ||
1297 | free(zUtf8); | ||
1298 | }else{ | ||
1299 | return SQLITE_NOMEM; | ||
1300 | } | ||
1301 | } | ||
1302 | for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} | ||
1303 | zTempPath[i] = 0; | ||
1304 | sqlite3_snprintf(pVfs->mxPathname-30, zBuf, | ||
1305 | "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); | ||
1306 | j = strlen(zBuf); | ||
1307 | sqlite3Randomness(20, &zBuf[j]); | ||
1308 | for(i=0; i<20; i++, j++){ | ||
1309 | zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; | ||
1310 | } | ||
1311 | zBuf[j] = 0; | ||
1312 | OSTRACE2("TEMP FILENAME: %s\n", zBuf); | ||
1313 | return SQLITE_OK; | ||
1314 | } | ||
1315 | |||
1316 | /* | ||
1317 | ** Turn a relative pathname into a full pathname. Write the full | ||
1318 | ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname | ||
1319 | ** bytes in size. | ||
1320 | */ | ||
1321 | static int winFullPathname( | ||
1322 | sqlite3_vfs *pVfs, /* Pointer to vfs object */ | ||
1323 | const char *zRelative, /* Possibly relative input path */ | ||
1324 | int nFull, /* Size of output buffer in bytes */ | ||
1325 | char *zFull /* Output buffer */ | ||
1326 | ){ | ||
1327 | |||
1328 | #if defined(__CYGWIN__) | ||
1329 | cygwin_conv_to_full_win32_path(zRelative, zFull); | ||
1330 | return SQLITE_OK; | ||
1331 | #endif | ||
1332 | |||
1333 | #if OS_WINCE | ||
1334 | /* WinCE has no concept of a relative pathname, or so I am told. */ | ||
1335 | sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative); | ||
1336 | #endif | ||
1337 | |||
1338 | #if !OS_WINCE && !defined(__CYGWIN__) | ||
1339 | int nByte; | ||
1340 | void *zConverted; | ||
1341 | char *zOut; | ||
1342 | zConverted = convertUtf8Filename(zRelative); | ||
1343 | if( isNT() ){ | ||
1344 | WCHAR *zTemp; | ||
1345 | nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; | ||
1346 | zTemp = malloc( nByte*sizeof(zTemp[0]) ); | ||
1347 | if( zTemp==0 ){ | ||
1348 | free(zConverted); | ||
1349 | return SQLITE_NOMEM; | ||
1350 | } | ||
1351 | GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); | ||
1352 | free(zConverted); | ||
1353 | zOut = unicodeToUtf8(zTemp); | ||
1354 | free(zTemp); | ||
1355 | }else{ | ||
1356 | char *zTemp; | ||
1357 | nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3; | ||
1358 | zTemp = malloc( nByte*sizeof(zTemp[0]) ); | ||
1359 | if( zTemp==0 ){ | ||
1360 | free(zConverted); | ||
1361 | return SQLITE_NOMEM; | ||
1362 | } | ||
1363 | GetFullPathNameA((char*)zConverted, nByte, zTemp, 0); | ||
1364 | free(zConverted); | ||
1365 | zOut = mbcsToUtf8(zTemp); | ||
1366 | free(zTemp); | ||
1367 | } | ||
1368 | if( zOut ){ | ||
1369 | sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut); | ||
1370 | free(zOut); | ||
1371 | return SQLITE_OK; | ||
1372 | }else{ | ||
1373 | return SQLITE_NOMEM; | ||
1374 | } | ||
1375 | #endif | ||
1376 | } | ||
1377 | |||
1378 | #ifndef SQLITE_OMIT_LOAD_EXTENSION | ||
1379 | /* | ||
1380 | ** Interfaces for opening a shared library, finding entry points | ||
1381 | ** within the shared library, and closing the shared library. | ||
1382 | */ | ||
1383 | /* | ||
1384 | ** Interfaces for opening a shared library, finding entry points | ||
1385 | ** within the shared library, and closing the shared library. | ||
1386 | */ | ||
1387 | static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ | ||
1388 | HANDLE h; | ||
1389 | void *zConverted = convertUtf8Filename(zFilename); | ||
1390 | if( zConverted==0 ){ | ||
1391 | return 0; | ||
1392 | } | ||
1393 | if( isNT() ){ | ||
1394 | h = LoadLibraryW((WCHAR*)zConverted); | ||
1395 | }else{ | ||
1396 | #if OS_WINCE | ||
1397 | return 0; | ||
1398 | #else | ||
1399 | h = LoadLibraryA((char*)zConverted); | ||
1400 | #endif | ||
1401 | } | ||
1402 | free(zConverted); | ||
1403 | return (void*)h; | ||
1404 | } | ||
1405 | static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ | ||
1406 | FormatMessage( | ||
1407 | FORMAT_MESSAGE_FROM_SYSTEM, | ||
1408 | NULL, | ||
1409 | GetLastError(), | ||
1410 | 0, | ||
1411 | zBufOut, | ||
1412 | nBuf-1, | ||
1413 | 0 | ||
1414 | ); | ||
1415 | } | ||
1416 | void *winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ | ||
1417 | #if OS_WINCE | ||
1418 | /* The GetProcAddressA() routine is only available on wince. */ | ||
1419 | return GetProcAddressA((HANDLE)pHandle, zSymbol); | ||
1420 | #else | ||
1421 | /* All other windows platforms expect GetProcAddress() to take | ||
1422 | ** an Ansi string regardless of the _UNICODE setting */ | ||
1423 | return GetProcAddress((HANDLE)pHandle, zSymbol); | ||
1424 | #endif | ||
1425 | } | ||
1426 | void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ | ||
1427 | FreeLibrary((HANDLE)pHandle); | ||
1428 | } | ||
1429 | #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ | ||
1430 | #define winDlOpen 0 | ||
1431 | #define winDlError 0 | ||
1432 | #define winDlSym 0 | ||
1433 | #define winDlClose 0 | ||
1434 | #endif | ||
1435 | |||
1436 | |||
1437 | /* | ||
1438 | ** Write up to nBuf bytes of randomness into zBuf. | ||
1439 | */ | ||
1440 | static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ | ||
1441 | int n = 0; | ||
1442 | if( sizeof(SYSTEMTIME)<=nBuf-n ){ | ||
1443 | SYSTEMTIME x; | ||
1444 | GetSystemTime(&x); | ||
1445 | memcpy(&zBuf[n], &x, sizeof(x)); | ||
1446 | n += sizeof(x); | ||
1447 | } | ||
1448 | if( sizeof(DWORD)<=nBuf-n ){ | ||
1449 | DWORD pid = GetCurrentProcessId(); | ||
1450 | memcpy(&zBuf[n], &pid, sizeof(pid)); | ||
1451 | n += sizeof(pid); | ||
1452 | } | ||
1453 | if( sizeof(DWORD)<=nBuf-n ){ | ||
1454 | DWORD cnt = GetTickCount(); | ||
1455 | memcpy(&zBuf[n], &cnt, sizeof(cnt)); | ||
1456 | n += sizeof(cnt); | ||
1457 | } | ||
1458 | if( sizeof(LARGE_INTEGER)<=nBuf-n ){ | ||
1459 | LARGE_INTEGER i; | ||
1460 | QueryPerformanceCounter(&i); | ||
1461 | memcpy(&zBuf[n], &i, sizeof(i)); | ||
1462 | n += sizeof(i); | ||
1463 | } | ||
1464 | return n; | ||
1465 | } | ||
1466 | |||
1467 | |||
1468 | /* | ||
1469 | ** Sleep for a little while. Return the amount of time slept. | ||
1470 | */ | ||
1471 | static int winSleep(sqlite3_vfs *pVfs, int microsec){ | ||
1472 | Sleep((microsec+999)/1000); | ||
1473 | return ((microsec+999)/1000)*1000; | ||
1474 | } | ||
1475 | |||
1476 | /* | ||
1477 | ** The following variable, if set to a non-zero value, becomes the result | ||
1478 | ** returned from sqlite3OsCurrentTime(). This is used for testing. | ||
1479 | */ | ||
1480 | #ifdef SQLITE_TEST | ||
1481 | int sqlite3_current_time = 0; | ||
1482 | #endif | ||
1483 | |||
1484 | /* | ||
1485 | ** Find the current time (in Universal Coordinated Time). Write the | ||
1486 | ** current time and date as a Julian Day number into *prNow and | ||
1487 | ** return 0. Return 1 if the time and date cannot be found. | ||
1488 | */ | ||
1489 | int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ | ||
1490 | FILETIME ft; | ||
1491 | /* FILETIME structure is a 64-bit value representing the number of | ||
1492 | 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). | ||
1493 | */ | ||
1494 | double now; | ||
1495 | #if OS_WINCE | ||
1496 | SYSTEMTIME time; | ||
1497 | GetSystemTime(&time); | ||
1498 | SystemTimeToFileTime(&time,&ft); | ||
1499 | #else | ||
1500 | GetSystemTimeAsFileTime( &ft ); | ||
1501 | #endif | ||
1502 | now = ((double)ft.dwHighDateTime) * 4294967296.0; | ||
1503 | *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; | ||
1504 | #ifdef SQLITE_TEST | ||
1505 | if( sqlite3_current_time ){ | ||
1506 | *prNow = sqlite3_current_time/86400.0 + 2440587.5; | ||
1507 | } | ||
1508 | #endif | ||
1509 | return 0; | ||
1510 | } | ||
1511 | |||
1512 | |||
1513 | /* | ||
1514 | ** Return a pointer to the sqlite3DefaultVfs structure. We use | ||
1515 | ** a function rather than give the structure global scope because | ||
1516 | ** some compilers (MSVC) do not allow forward declarations of | ||
1517 | ** initialized structures. | ||
1518 | */ | ||
1519 | sqlite3_vfs *sqlite3OsDefaultVfs(void){ | ||
1520 | static sqlite3_vfs winVfs = { | ||
1521 | 1, /* iVersion */ | ||
1522 | sizeof(winFile), /* szOsFile */ | ||
1523 | MAX_PATH, /* mxPathname */ | ||
1524 | 0, /* pNext */ | ||
1525 | "win32", /* zName */ | ||
1526 | 0, /* pAppData */ | ||
1527 | |||
1528 | winOpen, /* xOpen */ | ||
1529 | winDelete, /* xDelete */ | ||
1530 | winAccess, /* xAccess */ | ||
1531 | winGetTempname, /* xGetTempName */ | ||
1532 | winFullPathname, /* xFullPathname */ | ||
1533 | winDlOpen, /* xDlOpen */ | ||
1534 | winDlError, /* xDlError */ | ||
1535 | winDlSym, /* xDlSym */ | ||
1536 | winDlClose, /* xDlClose */ | ||
1537 | winRandomness, /* xRandomness */ | ||
1538 | winSleep, /* xSleep */ | ||
1539 | winCurrentTime /* xCurrentTime */ | ||
1540 | }; | ||
1541 | |||
1542 | return &winVfs; | ||
1543 | } | ||
1544 | |||
1545 | #endif /* OS_WIN */ | ||