summaryrefslogtreecommitdiffstats
path: root/common/unqlite/os_win.c
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
commit7cc25005b8c46d1fa783d33def2c6923e8ef8469 (patch)
tree64fa59d17af29838396cf37b912b3babd885e5dd /common/unqlite/os_win.c
parentbfc32f265e8ad72823db960fed371d72596003b7 (diff)
parenta6ed70495f9f3ecb21c26860dda16aadcdc91c3a (diff)
downloadsink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.tar.gz
sink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.zip
Merge branch 'unqlite'
Diffstat (limited to 'common/unqlite/os_win.c')
-rw-r--r--common/unqlite/os_win.c940
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__
32typedef 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*/
46typedef struct winFile winFile;
47struct 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*/
69static 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*/
90static 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*/
115static 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*/
136char *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*/
160static 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
193static 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*/
208static 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*/
237static 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*/
273static 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*/
299static 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*/
312static 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*/
337static 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*/
352static 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*/
386static 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*/
497static 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*/
523static 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*/
555static 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*/
562static 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*/
584static 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
604static 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*/
634static 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*/
691static 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*/
727static 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*/
770static 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 */
778static 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*/
803static 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*/
843static 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 */
922UNQLITE_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__ */