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