diff options
author | Aaron Seigo <aseigo@kde.org> | 2014-12-07 10:08:07 +0100 |
---|---|---|
committer | Aaron Seigo <aseigo@kde.org> | 2014-12-11 01:07:08 +0100 |
commit | 9ee8378d393778ac67314be7ea8d5bcbaeee9ee0 (patch) | |
tree | cf93471a69f9f4bbb4940de55ae134106fcd8380 /common/unqlite/os_win.c | |
parent | ee6f068dff6b15441e553ffbfb2bf8aa97b26f57 (diff) | |
download | sink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.tar.gz sink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.zip |
try out unqlite
Diffstat (limited to 'common/unqlite/os_win.c')
-rw-r--r-- | common/unqlite/os_win.c | 940 |
1 files changed, 940 insertions, 0 deletions
diff --git a/common/unqlite/os_win.c b/common/unqlite/os_win.c new file mode 100644 index 0000000..c1e0821 --- /dev/null +++ b/common/unqlite/os_win.c | |||
@@ -0,0 +1,940 @@ | |||
1 | /* | ||
2 | * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine. | ||
3 | * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/ | ||
4 | * Version 1.1.6 | ||
5 | * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES | ||
6 | * please contact Symisc Systems via: | ||
7 | * legal@symisc.net | ||
8 | * licensing@symisc.net | ||
9 | * contact@symisc.net | ||
10 | * or visit: | ||
11 | * http://unqlite.org/licensing.html | ||
12 | */ | ||
13 | /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel <chm@symisc.net> $ */ | ||
14 | #ifndef UNQLITE_AMALGAMATION | ||
15 | #include "unqliteInt.h" | ||
16 | #endif | ||
17 | /* Omit the whole layer from the build if compiling for platforms other than Windows */ | ||
18 | #ifdef __WINNT__ | ||
19 | /* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */ | ||
20 | #include <Windows.h> | ||
21 | /* | ||
22 | ** Some microsoft compilers lack this definition. | ||
23 | */ | ||
24 | #ifndef INVALID_FILE_ATTRIBUTES | ||
25 | # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) | ||
26 | #endif | ||
27 | /* | ||
28 | ** WinCE lacks native support for file locking so we have to fake it | ||
29 | ** with some code of our own. | ||
30 | */ | ||
31 | #ifdef __WIN_CE__ | ||
32 | typedef struct winceLock { | ||
33 | int nReaders; /* Number of reader locks obtained */ | ||
34 | BOOL bPending; /* Indicates a pending lock has been obtained */ | ||
35 | BOOL bReserved; /* Indicates a reserved lock has been obtained */ | ||
36 | BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ | ||
37 | } winceLock; | ||
38 | #define AreFileApisANSI() 1 | ||
39 | #define FormatMessageW(a,b,c,d,e,f,g) 0 | ||
40 | #endif | ||
41 | |||
42 | /* | ||
43 | ** The winFile structure is a subclass of unqlite_file* specific to the win32 | ||
44 | ** portability layer. | ||
45 | */ | ||
46 | typedef struct winFile winFile; | ||
47 | struct winFile { | ||
48 | const unqlite_io_methods *pMethod; /*** Must be first ***/ | ||
49 | unqlite_vfs *pVfs; /* The VFS used to open this file */ | ||
50 | HANDLE h; /* Handle for accessing the file */ | ||
51 | sxu8 locktype; /* Type of lock currently held on this file */ | ||
52 | short sharedLockByte; /* Randomly chosen byte used as a shared lock */ | ||
53 | DWORD lastErrno; /* The Windows errno from the last I/O error */ | ||
54 | DWORD sectorSize; /* Sector size of the device file is on */ | ||
55 | int szChunk; /* Chunk size */ | ||
56 | #ifdef __WIN_CE__ | ||
57 | WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ | ||
58 | HANDLE hMutex; /* Mutex used to control access to shared lock */ | ||
59 | HANDLE hShared; /* Shared memory segment used for locking */ | ||
60 | winceLock local; /* Locks obtained by this instance of winFile */ | ||
61 | winceLock *shared; /* Global shared lock memory for the file */ | ||
62 | #endif | ||
63 | }; | ||
64 | /* | ||
65 | ** Convert a UTF-8 string to microsoft unicode (UTF-16?). | ||
66 | ** | ||
67 | ** Space to hold the returned string is obtained from HeapAlloc(). | ||
68 | */ | ||
69 | static WCHAR *utf8ToUnicode(const char *zFilename){ | ||
70 | int nChar; | ||
71 | WCHAR *zWideFilename; | ||
72 | |||
73 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); | ||
74 | zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) ); | ||
75 | if( zWideFilename==0 ){ | ||
76 | return 0; | ||
77 | } | ||
78 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); | ||
79 | if( nChar==0 ){ | ||
80 | HeapFree(GetProcessHeap(),0,zWideFilename); | ||
81 | zWideFilename = 0; | ||
82 | } | ||
83 | return zWideFilename; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | ** Convert microsoft unicode to UTF-8. Space to hold the returned string is | ||
88 | ** obtained from malloc(). | ||
89 | */ | ||
90 | static char *unicodeToUtf8(const WCHAR *zWideFilename){ | ||
91 | int nByte; | ||
92 | char *zFilename; | ||
93 | |||
94 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); | ||
95 | zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte ); | ||
96 | if( zFilename==0 ){ | ||
97 | return 0; | ||
98 | } | ||
99 | nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, | ||
100 | 0, 0); | ||
101 | if( nByte == 0 ){ | ||
102 | HeapFree(GetProcessHeap(),0,zFilename); | ||
103 | zFilename = 0; | ||
104 | } | ||
105 | return zFilename; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | ** Convert an ansi string to microsoft unicode, based on the | ||
110 | ** current codepage settings for file apis. | ||
111 | ** | ||
112 | ** Space to hold the returned string is obtained | ||
113 | ** from malloc. | ||
114 | */ | ||
115 | static WCHAR *mbcsToUnicode(const char *zFilename){ | ||
116 | int nByte; | ||
117 | WCHAR *zMbcsFilename; | ||
118 | int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | ||
119 | |||
120 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR); | ||
121 | zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) ); | ||
122 | if( zMbcsFilename==0 ){ | ||
123 | return 0; | ||
124 | } | ||
125 | nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); | ||
126 | if( nByte==0 ){ | ||
127 | HeapFree(GetProcessHeap(),0,zMbcsFilename); | ||
128 | zMbcsFilename = 0; | ||
129 | } | ||
130 | return zMbcsFilename; | ||
131 | } | ||
132 | /* | ||
133 | ** Convert multibyte character string to UTF-8. Space to hold the | ||
134 | ** returned string is obtained from malloc(). | ||
135 | */ | ||
136 | char *unqlite_win32_mbcs_to_utf8(const char *zFilename){ | ||
137 | char *zFilenameUtf8; | ||
138 | WCHAR *zTmpWide; | ||
139 | |||
140 | zTmpWide = mbcsToUnicode(zFilename); | ||
141 | if( zTmpWide==0 ){ | ||
142 | return 0; | ||
143 | } | ||
144 | zFilenameUtf8 = unicodeToUtf8(zTmpWide); | ||
145 | HeapFree(GetProcessHeap(),0,zTmpWide); | ||
146 | return zFilenameUtf8; | ||
147 | } | ||
148 | /* | ||
149 | ** Some microsoft compilers lack this definition. | ||
150 | */ | ||
151 | #ifndef INVALID_SET_FILE_POINTER | ||
152 | # define INVALID_SET_FILE_POINTER ((DWORD)-1) | ||
153 | #endif | ||
154 | |||
155 | /* | ||
156 | ** Move the current position of the file handle passed as the first | ||
157 | ** argument to offset iOffset within the file. If successful, return 0. | ||
158 | ** Otherwise, set pFile->lastErrno and return non-zero. | ||
159 | */ | ||
160 | static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){ | ||
161 | LONG upperBits; /* Most sig. 32 bits of new offset */ | ||
162 | LONG lowerBits; /* Least sig. 32 bits of new offset */ | ||
163 | DWORD dwRet; /* Value returned by SetFilePointer() */ | ||
164 | |||
165 | upperBits = (LONG)((iOffset>>32) & 0x7fffffff); | ||
166 | lowerBits = (LONG)(iOffset & 0xffffffff); | ||
167 | |||
168 | /* API oddity: If successful, SetFilePointer() returns a dword | ||
169 | ** containing the lower 32-bits of the new file-offset. Or, if it fails, | ||
170 | ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, | ||
171 | ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine | ||
172 | ** whether an error has actually occured, it is also necessary to call | ||
173 | ** GetLastError(). | ||
174 | */ | ||
175 | dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); | ||
176 | if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ | ||
177 | pFile->lastErrno = GetLastError(); | ||
178 | return 1; | ||
179 | } | ||
180 | return 0; | ||
181 | } | ||
182 | /* | ||
183 | ** Close a file. | ||
184 | ** | ||
185 | ** It is reported that an attempt to close a handle might sometimes | ||
186 | ** fail. This is a very unreasonable result, but windows is notorious | ||
187 | ** for being unreasonable so I do not doubt that it might happen. If | ||
188 | ** the close fails, we pause for 100 milliseconds and try again. As | ||
189 | ** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before | ||
190 | ** giving up and returning an error. | ||
191 | */ | ||
192 | #define MX_CLOSE_ATTEMPT 3 | ||
193 | static int winClose(unqlite_file *id) | ||
194 | { | ||
195 | int rc, cnt = 0; | ||
196 | winFile *pFile = (winFile*)id; | ||
197 | do{ | ||
198 | rc = CloseHandle(pFile->h); | ||
199 | }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); | ||
200 | |||
201 | return rc ? UNQLITE_OK : UNQLITE_IOERR; | ||
202 | } | ||
203 | /* | ||
204 | ** Read data from a file into a buffer. Return UNQLITE_OK if all | ||
205 | ** bytes were read successfully and UNQLITE_IOERR if anything goes | ||
206 | ** wrong. | ||
207 | */ | ||
208 | static int winRead( | ||
209 | unqlite_file *id, /* File to read from */ | ||
210 | void *pBuf, /* Write content into this buffer */ | ||
211 | unqlite_int64 amt, /* Number of bytes to read */ | ||
212 | unqlite_int64 offset /* Begin reading at this offset */ | ||
213 | ){ | ||
214 | winFile *pFile = (winFile*)id; /* file handle */ | ||
215 | DWORD nRead; /* Number of bytes actually read from file */ | ||
216 | |||
217 | if( seekWinFile(pFile, offset) ){ | ||
218 | return UNQLITE_FULL; | ||
219 | } | ||
220 | if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){ | ||
221 | pFile->lastErrno = GetLastError(); | ||
222 | return UNQLITE_IOERR; | ||
223 | } | ||
224 | if( nRead<(DWORD)amt ){ | ||
225 | /* Unread parts of the buffer must be zero-filled */ | ||
226 | SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead)); | ||
227 | return UNQLITE_IOERR; | ||
228 | } | ||
229 | |||
230 | return UNQLITE_OK; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | ** Write data from a buffer into a file. Return UNQLITE_OK on success | ||
235 | ** or some other error code on failure. | ||
236 | */ | ||
237 | static int winWrite( | ||
238 | unqlite_file *id, /* File to write into */ | ||
239 | const void *pBuf, /* The bytes to be written */ | ||
240 | unqlite_int64 amt, /* Number of bytes to write */ | ||
241 | unqlite_int64 offset /* Offset into the file to begin writing at */ | ||
242 | ){ | ||
243 | int rc; /* True if error has occured, else false */ | ||
244 | winFile *pFile = (winFile*)id; /* File handle */ | ||
245 | |||
246 | rc = seekWinFile(pFile, offset); | ||
247 | if( rc==0 ){ | ||
248 | sxu8 *aRem = (sxu8 *)pBuf; /* Data yet to be written */ | ||
249 | unqlite_int64 nRem = amt; /* Number of bytes yet to be written */ | ||
250 | DWORD nWrite; /* Bytes written by each WriteFile() call */ | ||
251 | |||
252 | while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){ | ||
253 | aRem += nWrite; | ||
254 | nRem -= nWrite; | ||
255 | } | ||
256 | if( nRem>0 ){ | ||
257 | pFile->lastErrno = GetLastError(); | ||
258 | rc = 1; | ||
259 | } | ||
260 | } | ||
261 | if( rc ){ | ||
262 | if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ | ||
263 | return UNQLITE_FULL; | ||
264 | } | ||
265 | return UNQLITE_IOERR; | ||
266 | } | ||
267 | return UNQLITE_OK; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | ** Truncate an open file to a specified size | ||
272 | */ | ||
273 | static int winTruncate(unqlite_file *id, unqlite_int64 nByte){ | ||
274 | winFile *pFile = (winFile*)id; /* File handle object */ | ||
275 | int rc = UNQLITE_OK; /* Return code for this function */ | ||
276 | |||
277 | |||
278 | /* If the user has configured a chunk-size for this file, truncate the | ||
279 | ** file so that it consists of an integer number of chunks (i.e. the | ||
280 | ** actual file size after the operation may be larger than the requested | ||
281 | ** size). | ||
282 | */ | ||
283 | if( pFile->szChunk ){ | ||
284 | nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; | ||
285 | } | ||
286 | |||
287 | /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ | ||
288 | if( seekWinFile(pFile, nByte) ){ | ||
289 | rc = UNQLITE_IOERR; | ||
290 | }else if( 0==SetEndOfFile(pFile->h) ){ | ||
291 | pFile->lastErrno = GetLastError(); | ||
292 | rc = UNQLITE_IOERR; | ||
293 | } | ||
294 | return rc; | ||
295 | } | ||
296 | /* | ||
297 | ** Make sure all writes to a particular file are committed to disk. | ||
298 | */ | ||
299 | static int winSync(unqlite_file *id, int flags){ | ||
300 | winFile *pFile = (winFile*)id; | ||
301 | SXUNUSED(flags); /* MSVC warning */ | ||
302 | if( FlushFileBuffers(pFile->h) ){ | ||
303 | return UNQLITE_OK; | ||
304 | }else{ | ||
305 | pFile->lastErrno = GetLastError(); | ||
306 | return UNQLITE_IOERR; | ||
307 | } | ||
308 | } | ||
309 | /* | ||
310 | ** Determine the current size of a file in bytes | ||
311 | */ | ||
312 | static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){ | ||
313 | DWORD upperBits; | ||
314 | DWORD lowerBits; | ||
315 | winFile *pFile = (winFile*)id; | ||
316 | DWORD error; | ||
317 | lowerBits = GetFileSize(pFile->h, &upperBits); | ||
318 | if( (lowerBits == INVALID_FILE_SIZE) | ||
319 | && ((error = GetLastError()) != NO_ERROR) ) | ||
320 | { | ||
321 | pFile->lastErrno = error; | ||
322 | return UNQLITE_IOERR; | ||
323 | } | ||
324 | *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits; | ||
325 | return UNQLITE_OK; | ||
326 | } | ||
327 | /* | ||
328 | ** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. | ||
329 | */ | ||
330 | #ifndef LOCKFILE_FAIL_IMMEDIATELY | ||
331 | # define LOCKFILE_FAIL_IMMEDIATELY 1 | ||
332 | #endif | ||
333 | |||
334 | /* | ||
335 | ** Acquire a reader lock. | ||
336 | */ | ||
337 | static int getReadLock(winFile *pFile){ | ||
338 | int res; | ||
339 | OVERLAPPED ovlp; | ||
340 | ovlp.Offset = SHARED_FIRST; | ||
341 | ovlp.OffsetHigh = 0; | ||
342 | ovlp.hEvent = 0; | ||
343 | res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp); | ||
344 | if( res == 0 ){ | ||
345 | pFile->lastErrno = GetLastError(); | ||
346 | } | ||
347 | return res; | ||
348 | } | ||
349 | /* | ||
350 | ** Undo a readlock | ||
351 | */ | ||
352 | static int unlockReadLock(winFile *pFile){ | ||
353 | int res; | ||
354 | res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
355 | if( res == 0 ){ | ||
356 | pFile->lastErrno = GetLastError(); | ||
357 | } | ||
358 | return res; | ||
359 | } | ||
360 | /* | ||
361 | ** Lock the file with the lock specified by parameter locktype - one | ||
362 | ** of the following: | ||
363 | ** | ||
364 | ** (1) SHARED_LOCK | ||
365 | ** (2) RESERVED_LOCK | ||
366 | ** (3) PENDING_LOCK | ||
367 | ** (4) EXCLUSIVE_LOCK | ||
368 | ** | ||
369 | ** Sometimes when requesting one lock state, additional lock states | ||
370 | ** are inserted in between. The locking might fail on one of the later | ||
371 | ** transitions leaving the lock state different from what it started but | ||
372 | ** still short of its goal. The following chart shows the allowed | ||
373 | ** transitions and the inserted intermediate states: | ||
374 | ** | ||
375 | ** UNLOCKED -> SHARED | ||
376 | ** SHARED -> RESERVED | ||
377 | ** SHARED -> (PENDING) -> EXCLUSIVE | ||
378 | ** RESERVED -> (PENDING) -> EXCLUSIVE | ||
379 | ** PENDING -> EXCLUSIVE | ||
380 | ** | ||
381 | ** This routine will only increase a lock. The winUnlock() routine | ||
382 | ** erases all locks at once and returns us immediately to locking level 0. | ||
383 | ** It is not possible to lower the locking level one step at a time. You | ||
384 | ** must go straight to locking level 0. | ||
385 | */ | ||
386 | static int winLock(unqlite_file *id, int locktype){ | ||
387 | int rc = UNQLITE_OK; /* Return code from subroutines */ | ||
388 | int res = 1; /* Result of a windows lock call */ | ||
389 | int newLocktype; /* Set pFile->locktype to this value before exiting */ | ||
390 | int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ | ||
391 | winFile *pFile = (winFile*)id; | ||
392 | DWORD error = NO_ERROR; | ||
393 | |||
394 | /* If there is already a lock of this type or more restrictive on the | ||
395 | ** OsFile, do nothing. | ||
396 | */ | ||
397 | if( pFile->locktype>=locktype ){ | ||
398 | return UNQLITE_OK; | ||
399 | } | ||
400 | |||
401 | /* Make sure the locking sequence is correct | ||
402 | assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); | ||
403 | assert( locktype!=PENDING_LOCK ); | ||
404 | assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); | ||
405 | */ | ||
406 | /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or | ||
407 | ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of | ||
408 | ** the PENDING_LOCK byte is temporary. | ||
409 | */ | ||
410 | newLocktype = pFile->locktype; | ||
411 | if( (pFile->locktype==NO_LOCK) | ||
412 | || ( (locktype==EXCLUSIVE_LOCK) | ||
413 | && (pFile->locktype==RESERVED_LOCK)) | ||
414 | ){ | ||
415 | int cnt = 3; | ||
416 | while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ | ||
417 | /* Try 3 times to get the pending lock. The pending lock might be | ||
418 | ** held by another reader process who will release it momentarily. | ||
419 | */ | ||
420 | Sleep(1); | ||
421 | } | ||
422 | gotPendingLock = res; | ||
423 | if( !res ){ | ||
424 | error = GetLastError(); | ||
425 | } | ||
426 | } | ||
427 | |||
428 | /* Acquire a shared lock | ||
429 | */ | ||
430 | if( locktype==SHARED_LOCK && res ){ | ||
431 | /* assert( pFile->locktype==NO_LOCK ); */ | ||
432 | res = getReadLock(pFile); | ||
433 | if( res ){ | ||
434 | newLocktype = SHARED_LOCK; | ||
435 | }else{ | ||
436 | error = GetLastError(); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | /* Acquire a RESERVED lock | ||
441 | */ | ||
442 | if( locktype==RESERVED_LOCK && res ){ | ||
443 | /* assert( pFile->locktype==SHARED_LOCK ); */ | ||
444 | res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
445 | if( res ){ | ||
446 | newLocktype = RESERVED_LOCK; | ||
447 | }else{ | ||
448 | error = GetLastError(); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | /* Acquire a PENDING lock | ||
453 | */ | ||
454 | if( locktype==EXCLUSIVE_LOCK && res ){ | ||
455 | newLocktype = PENDING_LOCK; | ||
456 | gotPendingLock = 0; | ||
457 | } | ||
458 | |||
459 | /* Acquire an EXCLUSIVE lock | ||
460 | */ | ||
461 | if( locktype==EXCLUSIVE_LOCK && res ){ | ||
462 | /* assert( pFile->locktype>=SHARED_LOCK ); */ | ||
463 | res = unlockReadLock(pFile); | ||
464 | res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
465 | if( res ){ | ||
466 | newLocktype = EXCLUSIVE_LOCK; | ||
467 | }else{ | ||
468 | error = GetLastError(); | ||
469 | getReadLock(pFile); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /* If we are holding a PENDING lock that ought to be released, then | ||
474 | ** release it now. | ||
475 | */ | ||
476 | if( gotPendingLock && locktype==SHARED_LOCK ){ | ||
477 | UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); | ||
478 | } | ||
479 | |||
480 | /* Update the state of the lock has held in the file descriptor then | ||
481 | ** return the appropriate result code. | ||
482 | */ | ||
483 | if( res ){ | ||
484 | rc = UNQLITE_OK; | ||
485 | }else{ | ||
486 | pFile->lastErrno = error; | ||
487 | rc = UNQLITE_BUSY; | ||
488 | } | ||
489 | pFile->locktype = (sxu8)newLocktype; | ||
490 | return rc; | ||
491 | } | ||
492 | /* | ||
493 | ** This routine checks if there is a RESERVED lock held on the specified | ||
494 | ** file by this or any other process. If such a lock is held, return | ||
495 | ** non-zero, otherwise zero. | ||
496 | */ | ||
497 | static int winCheckReservedLock(unqlite_file *id, int *pResOut){ | ||
498 | int rc; | ||
499 | winFile *pFile = (winFile*)id; | ||
500 | if( pFile->locktype>=RESERVED_LOCK ){ | ||
501 | rc = 1; | ||
502 | }else{ | ||
503 | rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
504 | if( rc ){ | ||
505 | UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
506 | } | ||
507 | rc = !rc; | ||
508 | } | ||
509 | *pResOut = rc; | ||
510 | return UNQLITE_OK; | ||
511 | } | ||
512 | /* | ||
513 | ** Lower the locking level on file descriptor id to locktype. locktype | ||
514 | ** must be either NO_LOCK or SHARED_LOCK. | ||
515 | ** | ||
516 | ** If the locking level of the file descriptor is already at or below | ||
517 | ** the requested locking level, this routine is a no-op. | ||
518 | ** | ||
519 | ** It is not possible for this routine to fail if the second argument | ||
520 | ** is NO_LOCK. If the second argument is SHARED_LOCK then this routine | ||
521 | ** might return UNQLITE_IOERR; | ||
522 | */ | ||
523 | static int winUnlock(unqlite_file *id, int locktype){ | ||
524 | int type; | ||
525 | winFile *pFile = (winFile*)id; | ||
526 | int rc = UNQLITE_OK; | ||
527 | |||
528 | type = pFile->locktype; | ||
529 | if( type>=EXCLUSIVE_LOCK ){ | ||
530 | UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); | ||
531 | if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ | ||
532 | /* This should never happen. We should always be able to | ||
533 | ** reacquire the read lock */ | ||
534 | rc = UNQLITE_IOERR; | ||
535 | } | ||
536 | } | ||
537 | if( type>=RESERVED_LOCK ){ | ||
538 | UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); | ||
539 | } | ||
540 | if( locktype==NO_LOCK && type>=SHARED_LOCK ){ | ||
541 | unlockReadLock(pFile); | ||
542 | } | ||
543 | if( type>=PENDING_LOCK ){ | ||
544 | UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); | ||
545 | } | ||
546 | pFile->locktype = (sxu8)locktype; | ||
547 | return rc; | ||
548 | } | ||
549 | /* | ||
550 | ** Return the sector size in bytes of the underlying block device for | ||
551 | ** the specified file. This is almost always 512 bytes, but may be | ||
552 | ** larger for some devices. | ||
553 | ** | ||
554 | */ | ||
555 | static int winSectorSize(unqlite_file *id){ | ||
556 | return (int)(((winFile*)id)->sectorSize); | ||
557 | } | ||
558 | /* | ||
559 | ** This vector defines all the methods that can operate on an | ||
560 | ** unqlite_file for Windows systems. | ||
561 | */ | ||
562 | static const unqlite_io_methods winIoMethod = { | ||
563 | 1, /* iVersion */ | ||
564 | winClose, /* xClose */ | ||
565 | winRead, /* xRead */ | ||
566 | winWrite, /* xWrite */ | ||
567 | winTruncate, /* xTruncate */ | ||
568 | winSync, /* xSync */ | ||
569 | winFileSize, /* xFileSize */ | ||
570 | winLock, /* xLock */ | ||
571 | winUnlock, /* xUnlock */ | ||
572 | winCheckReservedLock, /* xCheckReservedLock */ | ||
573 | winSectorSize, /* xSectorSize */ | ||
574 | }; | ||
575 | /* | ||
576 | * Windows VFS Methods. | ||
577 | */ | ||
578 | /* | ||
579 | ** Convert a UTF-8 filename into whatever form the underlying | ||
580 | ** operating system wants filenames in. Space to hold the result | ||
581 | ** is obtained from malloc and must be freed by the calling | ||
582 | ** function. | ||
583 | */ | ||
584 | static void *convertUtf8Filename(const char *zFilename) | ||
585 | { | ||
586 | void *zConverted; | ||
587 | zConverted = utf8ToUnicode(zFilename); | ||
588 | /* caller will handle out of memory */ | ||
589 | return zConverted; | ||
590 | } | ||
591 | /* | ||
592 | ** Delete the named file. | ||
593 | ** | ||
594 | ** Note that windows does not allow a file to be deleted if some other | ||
595 | ** process has it open. Sometimes a virus scanner or indexing program | ||
596 | ** will open a journal file shortly after it is created in order to do | ||
597 | ** whatever it does. While this other process is holding the | ||
598 | ** file open, we will be unable to delete it. To work around this | ||
599 | ** problem, we delay 100 milliseconds and try to delete again. Up | ||
600 | ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving | ||
601 | ** up and returning an error. | ||
602 | */ | ||
603 | #define MX_DELETION_ATTEMPTS 5 | ||
604 | static int winDelete( | ||
605 | unqlite_vfs *pVfs, /* Not used on win32 */ | ||
606 | const char *zFilename, /* Name of file to delete */ | ||
607 | int syncDir /* Not used on win32 */ | ||
608 | ){ | ||
609 | int cnt = 0; | ||
610 | DWORD rc; | ||
611 | DWORD error = 0; | ||
612 | void *zConverted; | ||
613 | zConverted = convertUtf8Filename(zFilename); | ||
614 | if( zConverted==0 ){ | ||
615 | SXUNUSED(pVfs); | ||
616 | SXUNUSED(syncDir); | ||
617 | return UNQLITE_NOMEM; | ||
618 | } | ||
619 | do{ | ||
620 | DeleteFileW((LPCWSTR)zConverted); | ||
621 | }while( ( ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES) | ||
622 | || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) | ||
623 | && (++cnt < MX_DELETION_ATTEMPTS) | ||
624 | && (Sleep(100), 1) | ||
625 | ); | ||
626 | HeapFree(GetProcessHeap(),0,zConverted); | ||
627 | |||
628 | return ( (rc == INVALID_FILE_ATTRIBUTES) | ||
629 | && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR; | ||
630 | } | ||
631 | /* | ||
632 | ** Check the existance and status of a file. | ||
633 | */ | ||
634 | static int winAccess( | ||
635 | unqlite_vfs *pVfs, /* Not used */ | ||
636 | const char *zFilename, /* Name of file to check */ | ||
637 | int flags, /* Type of test to make on this file */ | ||
638 | int *pResOut /* OUT: Result */ | ||
639 | ){ | ||
640 | WIN32_FILE_ATTRIBUTE_DATA sAttrData; | ||
641 | DWORD attr; | ||
642 | int rc = 0; | ||
643 | void *zConverted; | ||
644 | SXUNUSED(pVfs); | ||
645 | |||
646 | zConverted = convertUtf8Filename(zFilename); | ||
647 | if( zConverted==0 ){ | ||
648 | return UNQLITE_NOMEM; | ||
649 | } | ||
650 | SyZero(&sAttrData,sizeof(sAttrData)); | ||
651 | if( GetFileAttributesExW((WCHAR*)zConverted, | ||
652 | GetFileExInfoStandard, | ||
653 | &sAttrData) ){ | ||
654 | /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file | ||
655 | ** as if it does not exist. | ||
656 | */ | ||
657 | if( flags==UNQLITE_ACCESS_EXISTS | ||
658 | && sAttrData.nFileSizeHigh==0 | ||
659 | && sAttrData.nFileSizeLow==0 ){ | ||
660 | attr = INVALID_FILE_ATTRIBUTES; | ||
661 | }else{ | ||
662 | attr = sAttrData.dwFileAttributes; | ||
663 | } | ||
664 | }else{ | ||
665 | if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ | ||
666 | HeapFree(GetProcessHeap(),0,zConverted); | ||
667 | return UNQLITE_IOERR; | ||
668 | }else{ | ||
669 | attr = INVALID_FILE_ATTRIBUTES; | ||
670 | } | ||
671 | } | ||
672 | HeapFree(GetProcessHeap(),0,zConverted); | ||
673 | switch( flags ){ | ||
674 | case UNQLITE_ACCESS_READWRITE: | ||
675 | rc = (attr & FILE_ATTRIBUTE_READONLY)==0; | ||
676 | break; | ||
677 | case UNQLITE_ACCESS_READ: | ||
678 | case UNQLITE_ACCESS_EXISTS: | ||
679 | default: | ||
680 | rc = attr!=INVALID_FILE_ATTRIBUTES; | ||
681 | break; | ||
682 | } | ||
683 | *pResOut = rc; | ||
684 | return UNQLITE_OK; | ||
685 | } | ||
686 | /* | ||
687 | ** Turn a relative pathname into a full pathname. Write the full | ||
688 | ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname | ||
689 | ** bytes in size. | ||
690 | */ | ||
691 | static int winFullPathname( | ||
692 | unqlite_vfs *pVfs, /* Pointer to vfs object */ | ||
693 | const char *zRelative, /* Possibly relative input path */ | ||
694 | int nFull, /* Size of output buffer in bytes */ | ||
695 | char *zFull /* Output buffer */ | ||
696 | ){ | ||
697 | int nByte; | ||
698 | void *zConverted; | ||
699 | WCHAR *zTemp; | ||
700 | char *zOut; | ||
701 | SXUNUSED(nFull); | ||
702 | zConverted = convertUtf8Filename(zRelative); | ||
703 | if( zConverted == 0 ){ | ||
704 | return UNQLITE_NOMEM; | ||
705 | } | ||
706 | nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; | ||
707 | zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) ); | ||
708 | if( zTemp==0 ){ | ||
709 | HeapFree(GetProcessHeap(),0,zConverted); | ||
710 | return UNQLITE_NOMEM; | ||
711 | } | ||
712 | GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); | ||
713 | HeapFree(GetProcessHeap(),0,zConverted); | ||
714 | zOut = unicodeToUtf8(zTemp); | ||
715 | HeapFree(GetProcessHeap(),0,zTemp); | ||
716 | if( zOut == 0 ){ | ||
717 | return UNQLITE_NOMEM; | ||
718 | } | ||
719 | Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0); | ||
720 | HeapFree(GetProcessHeap(),0,zOut); | ||
721 | return UNQLITE_OK; | ||
722 | } | ||
723 | /* | ||
724 | ** Get the sector size of the device used to store | ||
725 | ** file. | ||
726 | */ | ||
727 | static int getSectorSize( | ||
728 | unqlite_vfs *pVfs, | ||
729 | const char *zRelative /* UTF-8 file name */ | ||
730 | ){ | ||
731 | DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; | ||
732 | char zFullpath[MAX_PATH+1]; | ||
733 | int rc; | ||
734 | DWORD dwRet = 0; | ||
735 | DWORD dwDummy; | ||
736 | /* | ||
737 | ** We need to get the full path name of the file | ||
738 | ** to get the drive letter to look up the sector | ||
739 | ** size. | ||
740 | */ | ||
741 | rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); | ||
742 | if( rc == UNQLITE_OK ) | ||
743 | { | ||
744 | void *zConverted = convertUtf8Filename(zFullpath); | ||
745 | if( zConverted ){ | ||
746 | /* trim path to just drive reference */ | ||
747 | WCHAR *p = (WCHAR *)zConverted; | ||
748 | for(;*p;p++){ | ||
749 | if( *p == '\\' ){ | ||
750 | *p = '\0'; | ||
751 | break; | ||
752 | } | ||
753 | } | ||
754 | dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, | ||
755 | &dwDummy, | ||
756 | &bytesPerSector, | ||
757 | &dwDummy, | ||
758 | &dwDummy); | ||
759 | HeapFree(GetProcessHeap(),0,zConverted); | ||
760 | } | ||
761 | if( !dwRet ){ | ||
762 | bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE; | ||
763 | } | ||
764 | } | ||
765 | return (int) bytesPerSector; | ||
766 | } | ||
767 | /* | ||
768 | ** Sleep for a little while. Return the amount of time slept. | ||
769 | */ | ||
770 | static int winSleep(unqlite_vfs *pVfs, int microsec){ | ||
771 | Sleep((microsec+999)/1000); | ||
772 | SXUNUSED(pVfs); | ||
773 | return ((microsec+999)/1000)*1000; | ||
774 | } | ||
775 | /* | ||
776 | * Export the current system time. | ||
777 | */ | ||
778 | static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut) | ||
779 | { | ||
780 | SYSTEMTIME sSys; | ||
781 | SXUNUSED(pVfs); | ||
782 | GetSystemTime(&sSys); | ||
783 | SYSTEMTIME_TO_SYTM(&sSys,pOut); | ||
784 | return UNQLITE_OK; | ||
785 | } | ||
786 | /* | ||
787 | ** The idea is that this function works like a combination of | ||
788 | ** GetLastError() and FormatMessage() on windows (or errno and | ||
789 | ** strerror_r() on unix). After an error is returned by an OS | ||
790 | ** function, UnQLite calls this function with zBuf pointing to | ||
791 | ** a buffer of nBuf bytes. The OS layer should populate the | ||
792 | ** buffer with a nul-terminated UTF-8 encoded error message | ||
793 | ** describing the last IO error to have occurred within the calling | ||
794 | ** thread. | ||
795 | ** | ||
796 | ** If the error message is too large for the supplied buffer, | ||
797 | ** it should be truncated. The return value of xGetLastError | ||
798 | ** is zero if the error message fits in the buffer, or non-zero | ||
799 | ** otherwise (if the message was truncated). If non-zero is returned, | ||
800 | ** then it is not necessary to include the nul-terminator character | ||
801 | ** in the output buffer. | ||
802 | */ | ||
803 | static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf) | ||
804 | { | ||
805 | /* FormatMessage returns 0 on failure. Otherwise it | ||
806 | ** returns the number of TCHARs written to the output | ||
807 | ** buffer, excluding the terminating null char. | ||
808 | */ | ||
809 | DWORD error = GetLastError(); | ||
810 | WCHAR *zTempWide = 0; | ||
811 | DWORD dwLen; | ||
812 | char *zOut = 0; | ||
813 | |||
814 | SXUNUSED(pVfs); | ||
815 | dwLen = FormatMessageW( | ||
816 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||
817 | 0, | ||
818 | error, | ||
819 | 0, | ||
820 | (LPWSTR) &zTempWide, | ||
821 | 0, | ||
822 | 0 | ||
823 | ); | ||
824 | if( dwLen > 0 ){ | ||
825 | /* allocate a buffer and convert to UTF8 */ | ||
826 | zOut = unicodeToUtf8(zTempWide); | ||
827 | /* free the system buffer allocated by FormatMessage */ | ||
828 | LocalFree(zTempWide); | ||
829 | } | ||
830 | if( 0 == dwLen ){ | ||
831 | Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1); | ||
832 | }else{ | ||
833 | /* copy a maximum of nBuf chars to output buffer */ | ||
834 | Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */); | ||
835 | /* free the UTF8 buffer */ | ||
836 | HeapFree(GetProcessHeap(),0,zOut); | ||
837 | } | ||
838 | return 0; | ||
839 | } | ||
840 | /* | ||
841 | ** Open a file. | ||
842 | */ | ||
843 | static int winOpen( | ||
844 | unqlite_vfs *pVfs, /* Not used */ | ||
845 | const char *zName, /* Name of the file (UTF-8) */ | ||
846 | unqlite_file *id, /* Write the UnQLite file handle here */ | ||
847 | unsigned int flags /* Open mode flags */ | ||
848 | ){ | ||
849 | HANDLE h; | ||
850 | DWORD dwDesiredAccess; | ||
851 | DWORD dwShareMode; | ||
852 | DWORD dwCreationDisposition; | ||
853 | DWORD dwFlagsAndAttributes = 0; | ||
854 | winFile *pFile = (winFile*)id; | ||
855 | void *zConverted; /* Filename in OS encoding */ | ||
856 | const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ | ||
857 | int isExclusive = (flags & UNQLITE_OPEN_EXCLUSIVE); | ||
858 | int isDelete = (flags & UNQLITE_OPEN_TEMP_DB); | ||
859 | int isCreate = (flags & UNQLITE_OPEN_CREATE); | ||
860 | int isReadWrite = (flags & UNQLITE_OPEN_READWRITE); | ||
861 | |||
862 | pFile->h = INVALID_HANDLE_VALUE; | ||
863 | /* Convert the filename to the system encoding. */ | ||
864 | zConverted = convertUtf8Filename(zUtf8Name); | ||
865 | if( zConverted==0 ){ | ||
866 | return UNQLITE_NOMEM; | ||
867 | } | ||
868 | if( isReadWrite ){ | ||
869 | dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; | ||
870 | }else{ | ||
871 | dwDesiredAccess = GENERIC_READ; | ||
872 | } | ||
873 | /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is | ||
874 | ** created. | ||
875 | */ | ||
876 | if( isExclusive ){ | ||
877 | /* Creates a new file, only if it does not already exist. */ | ||
878 | /* If the file exists, it fails. */ | ||
879 | dwCreationDisposition = CREATE_NEW; | ||
880 | }else if( isCreate ){ | ||
881 | /* Open existing file, or create if it doesn't exist */ | ||
882 | dwCreationDisposition = OPEN_ALWAYS; | ||
883 | }else{ | ||
884 | /* Opens a file, only if it exists. */ | ||
885 | dwCreationDisposition = OPEN_EXISTING; | ||
886 | } | ||
887 | |||
888 | dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | ||
889 | |||
890 | if( isDelete ){ | ||
891 | dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | ||
892 | | FILE_ATTRIBUTE_HIDDEN | ||
893 | | FILE_FLAG_DELETE_ON_CLOSE; | ||
894 | }else{ | ||
895 | dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; | ||
896 | } | ||
897 | h = CreateFileW((WCHAR*)zConverted, | ||
898 | dwDesiredAccess, | ||
899 | dwShareMode, | ||
900 | NULL, | ||
901 | dwCreationDisposition, | ||
902 | dwFlagsAndAttributes, | ||
903 | NULL | ||
904 | ); | ||
905 | if( h==INVALID_HANDLE_VALUE ){ | ||
906 | pFile->lastErrno = GetLastError(); | ||
907 | HeapFree(GetProcessHeap(),0,zConverted); | ||
908 | return UNQLITE_IOERR; | ||
909 | } | ||
910 | SyZero(pFile,sizeof(*pFile)); | ||
911 | pFile->pMethod = &winIoMethod; | ||
912 | pFile->h = h; | ||
913 | pFile->lastErrno = NO_ERROR; | ||
914 | pFile->pVfs = pVfs; | ||
915 | pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); | ||
916 | HeapFree(GetProcessHeap(),0,zConverted); | ||
917 | return UNQLITE_OK; | ||
918 | } | ||
919 | /* | ||
920 | * Export the Windows Vfs. | ||
921 | */ | ||
922 | UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void) | ||
923 | { | ||
924 | static const unqlite_vfs sWinvfs = { | ||
925 | "Windows", /* Vfs name */ | ||
926 | 1, /* Vfs structure version */ | ||
927 | sizeof(winFile), /* szOsFile */ | ||
928 | MAX_PATH, /* mxPathName */ | ||
929 | winOpen, /* xOpen */ | ||
930 | winDelete, /* xDelete */ | ||
931 | winAccess, /* xAccess */ | ||
932 | winFullPathname, /* xFullPathname */ | ||
933 | 0, /* xTmp */ | ||
934 | winSleep, /* xSleep */ | ||
935 | winCurrentTime, /* xCurrentTime */ | ||
936 | winGetLastError, /* xGetLastError */ | ||
937 | }; | ||
938 | return &sWinvfs; | ||
939 | } | ||
940 | #endif /* __WINNT__ */ | ||