xref: /sqlite-3.40.0/ext/lsm1/lsm_win32.c (revision 1be50519)
1 /*
2 ** 2011-12-03
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 ** Win32-specific run-time environment implementation for LSM.
14 */
15 
16 #ifdef _WIN32
17 
18 #include <assert.h>
19 #include <string.h>
20 
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 
26 #include "windows.h"
27 
28 #include "lsmInt.h"
29 
30 /*
31 ** An open file is an instance of the following object
32 */
33 typedef struct Win32File Win32File;
34 struct Win32File {
35   lsm_env *pEnv;                  /* The run-time environment */
36   const char *zName;              /* Full path to file */
37 
38   HANDLE hFile;                   /* Open file handle */
39   HANDLE hShmFile;                /* File handle for *-shm file */
40 
41   SYSTEM_INFO sysInfo;            /* Operating system information */
42   HANDLE hMap;                    /* File handle for mapping */
43   LPVOID pMap;                    /* Pointer to mapping of file fd */
44   size_t nMap;                    /* Size of mapping at pMap in bytes */
45   int nShm;                       /* Number of entries in ahShm[]/apShm[] */
46   LPHANDLE ahShm;                 /* Array of handles for shared mappings */
47   LPVOID *apShm;                  /* Array of 32K shared memory segments */
48 };
49 
win32ShmFile(Win32File * pWin32File)50 static char *win32ShmFile(Win32File *pWin32File){
51   char *zShm;
52   int nName = strlen(pWin32File->zName);
53   zShm = (char *)lsmMallocZero(pWin32File->pEnv, nName+4+1);
54   if( zShm ){
55     memcpy(zShm, pWin32File->zName, nName);
56     memcpy(&zShm[nName], "-shm", 5);
57   }
58   return zShm;
59 }
60 
win32Sleep(int us)61 static int win32Sleep(int us){
62   Sleep((us + 999) / 1000);
63   return LSM_OK;
64 }
65 
66 /*
67 ** The number of times that an I/O operation will be retried following a
68 ** locking error - probably caused by antivirus software.  Also the initial
69 ** delay before the first retry.  The delay increases linearly with each
70 ** retry.
71 */
72 #ifndef LSM_WIN32_IOERR_RETRY
73 # define LSM_WIN32_IOERR_RETRY 10
74 #endif
75 #ifndef LSM_WIN32_IOERR_RETRY_DELAY
76 # define LSM_WIN32_IOERR_RETRY_DELAY 25000
77 #endif
78 static int win32IoerrRetry = LSM_WIN32_IOERR_RETRY;
79 static int win32IoerrRetryDelay = LSM_WIN32_IOERR_RETRY_DELAY;
80 
81 /*
82 ** The "win32IoerrCanRetry1" macro is used to determine if a particular
83 ** I/O error code obtained via GetLastError() is eligible to be retried.
84 ** It must accept the error code DWORD as its only argument and should
85 ** return non-zero if the error code is transient in nature and the
86 ** operation responsible for generating the original error might succeed
87 ** upon being retried.  The argument to this macro should be a variable.
88 **
89 ** Additionally, a macro named "win32IoerrCanRetry2" may be defined.  If
90 ** it is defined, it will be consulted only when the macro
91 ** "win32IoerrCanRetry1" returns zero.  The "win32IoerrCanRetry2" macro
92 ** is completely optional and may be used to include additional error
93 ** codes in the set that should result in the failing I/O operation being
94 ** retried by the caller.  If defined, the "win32IoerrCanRetry2" macro
95 ** must exhibit external semantics identical to those of the
96 ** "win32IoerrCanRetry1" macro.
97 */
98 #if !defined(win32IoerrCanRetry1)
99 #define win32IoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED)        || \
100                                 ((a)==ERROR_SHARING_VIOLATION)    || \
101                                 ((a)==ERROR_LOCK_VIOLATION)       || \
102                                 ((a)==ERROR_DEV_NOT_EXIST)        || \
103                                 ((a)==ERROR_NETNAME_DELETED)      || \
104                                 ((a)==ERROR_SEM_TIMEOUT)          || \
105                                 ((a)==ERROR_NETWORK_UNREACHABLE))
106 #endif
107 
108 /*
109 ** If an I/O error occurs, invoke this routine to see if it should be
110 ** retried.  Return TRUE to retry.  Return FALSE to give up with an
111 ** error.
112 */
win32RetryIoerr(lsm_env * pEnv,int * pnRetry)113 static int win32RetryIoerr(
114   lsm_env *pEnv,
115   int *pnRetry
116 ){
117   DWORD lastErrno;
118   if( *pnRetry>=win32IoerrRetry ){
119     return 0;
120   }
121   lastErrno = GetLastError();
122   if( win32IoerrCanRetry1(lastErrno) ){
123     win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
124     ++*pnRetry;
125     return 1;
126   }
127 #if defined(win32IoerrCanRetry2)
128   else if( win32IoerrCanRetry2(lastErrno) ){
129     win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
130     ++*pnRetry;
131     return 1;
132   }
133 #endif
134   return 0;
135 }
136 
137 /*
138 ** Convert a UTF-8 string to Microsoft Unicode.
139 **
140 ** Space to hold the returned string is obtained from lsmMalloc().
141 */
win32Utf8ToUnicode(lsm_env * pEnv,const char * zText)142 static LPWSTR win32Utf8ToUnicode(lsm_env *pEnv, const char *zText){
143   int nChar;
144   LPWSTR zWideText;
145 
146   nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
147   if( nChar==0 ){
148     return 0;
149   }
150   zWideText = lsmMallocZero(pEnv, nChar * sizeof(WCHAR));
151   if( zWideText==0 ){
152     return 0;
153   }
154   nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, nChar);
155   if( nChar==0 ){
156     lsmFree(pEnv, zWideText);
157     zWideText = 0;
158   }
159   return zWideText;
160 }
161 
162 /*
163 ** Convert a Microsoft Unicode string to UTF-8.
164 **
165 ** Space to hold the returned string is obtained from lsmMalloc().
166 */
win32UnicodeToUtf8(lsm_env * pEnv,LPCWSTR zWideText)167 static char *win32UnicodeToUtf8(lsm_env *pEnv, LPCWSTR zWideText){
168   int nByte;
169   char *zText;
170 
171   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
172   if( nByte == 0 ){
173     return 0;
174   }
175   zText = lsmMallocZero(pEnv, nByte);
176   if( zText==0 ){
177     return 0;
178   }
179   nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, 0, 0);
180   if( nByte == 0 ){
181     lsmFree(pEnv, zText);
182     zText = 0;
183   }
184   return zText;
185 }
186 
187 #if !defined(win32IsNotFound)
188 #define win32IsNotFound(a) (((a)==ERROR_FILE_NOT_FOUND)  || \
189                             ((a)==ERROR_PATH_NOT_FOUND))
190 #endif
191 
win32Open(lsm_env * pEnv,const char * zFile,int flags,LPHANDLE phFile)192 static int win32Open(
193   lsm_env *pEnv,
194   const char *zFile,
195   int flags,
196   LPHANDLE phFile
197 ){
198   int rc;
199   LPWSTR zConverted;
200 
201   zConverted = win32Utf8ToUnicode(pEnv, zFile);
202   if( zConverted==0 ){
203     rc = LSM_NOMEM_BKPT;
204   }else{
205     int bReadonly = (flags & LSM_OPEN_READONLY);
206     DWORD dwDesiredAccess;
207     DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
208     DWORD dwCreationDisposition;
209     DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
210     HANDLE hFile;
211     int nRetry = 0;
212     if( bReadonly ){
213       dwDesiredAccess = GENERIC_READ;
214       dwCreationDisposition = OPEN_EXISTING;
215     }else{
216       dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
217       dwCreationDisposition = OPEN_ALWAYS;
218     }
219     while( (hFile = CreateFileW((LPCWSTR)zConverted,
220                                 dwDesiredAccess,
221                                 dwShareMode, NULL,
222                                 dwCreationDisposition,
223                                 dwFlagsAndAttributes,
224                                 NULL))==INVALID_HANDLE_VALUE &&
225                                 win32RetryIoerr(pEnv, &nRetry) ){
226       /* Noop */
227     }
228     lsmFree(pEnv, zConverted);
229     if( hFile!=INVALID_HANDLE_VALUE ){
230       *phFile = hFile;
231       rc = LSM_OK;
232     }else{
233       if( win32IsNotFound(GetLastError()) ){
234         rc = lsmErrorBkpt(LSM_IOERR_NOENT);
235       }else{
236         rc = LSM_IOERR_BKPT;
237       }
238     }
239   }
240   return rc;
241 }
242 
lsmWin32OsOpen(lsm_env * pEnv,const char * zFile,int flags,lsm_file ** ppFile)243 static int lsmWin32OsOpen(
244   lsm_env *pEnv,
245   const char *zFile,
246   int flags,
247   lsm_file **ppFile
248 ){
249   int rc = LSM_OK;
250   Win32File *pWin32File;
251 
252   pWin32File = lsmMallocZero(pEnv, sizeof(Win32File));
253   if( pWin32File==0 ){
254     rc = LSM_NOMEM_BKPT;
255   }else{
256     HANDLE hFile = NULL;
257 
258     rc = win32Open(pEnv, zFile, flags, &hFile);
259     if( rc==LSM_OK ){
260       memset(&pWin32File->sysInfo, 0, sizeof(SYSTEM_INFO));
261       GetSystemInfo(&pWin32File->sysInfo);
262       pWin32File->pEnv = pEnv;
263       pWin32File->zName = zFile;
264       pWin32File->hFile = hFile;
265     }else{
266       lsmFree(pEnv, pWin32File);
267       pWin32File = 0;
268     }
269   }
270   *ppFile = (lsm_file *)pWin32File;
271   return rc;
272 }
273 
lsmWin32OsWrite(lsm_file * pFile,lsm_i64 iOff,void * pData,int nData)274 static int lsmWin32OsWrite(
275   lsm_file *pFile, /* File to write to */
276   lsm_i64 iOff,    /* Offset to write to */
277   void *pData,     /* Write data from this buffer */
278   int nData        /* Bytes of data to write */
279 ){
280   Win32File *pWin32File = (Win32File *)pFile;
281   OVERLAPPED overlapped;  /* The offset for WriteFile. */
282   u8 *aRem = (u8 *)pData; /* Data yet to be written */
283   int nRem = nData;       /* Number of bytes yet to be written */
284   int nRetry = 0;         /* Number of retrys */
285 
286   memset(&overlapped, 0, sizeof(OVERLAPPED));
287   overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
288   overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
289   while( nRem>0 ){
290     DWORD nWrite = 0; /* Bytes written using WriteFile */
291     if( !WriteFile(pWin32File->hFile, aRem, nRem, &nWrite, &overlapped) ){
292       if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
293       break;
294     }
295     assert( nWrite==0 || nWrite<=(DWORD)nRem );
296     if( nWrite==0 || nWrite>(DWORD)nRem ){
297       break;
298     }
299     iOff += nWrite;
300     overlapped.Offset = (LONG)(iOff & 0xFFFFFFFF);
301     overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
302     aRem += nWrite;
303     nRem -= nWrite;
304   }
305   if( nRem!=0 ) return LSM_IOERR_BKPT;
306   return LSM_OK;
307 }
308 
win32Truncate(HANDLE hFile,lsm_i64 nSize)309 static int win32Truncate(
310   HANDLE hFile,
311   lsm_i64 nSize
312 ){
313   LARGE_INTEGER offset;
314   offset.QuadPart = nSize;
315   if( !SetFilePointerEx(hFile, offset, 0, FILE_BEGIN) ){
316     return LSM_IOERR_BKPT;
317   }
318   if (!SetEndOfFile(hFile) ){
319     return LSM_IOERR_BKPT;
320   }
321   return LSM_OK;
322 }
323 
lsmWin32OsTruncate(lsm_file * pFile,lsm_i64 nSize)324 static int lsmWin32OsTruncate(
325   lsm_file *pFile, /* File to write to */
326   lsm_i64 nSize    /* Size to truncate file to */
327 ){
328   Win32File *pWin32File = (Win32File *)pFile;
329   return win32Truncate(pWin32File->hFile, nSize);
330 }
331 
lsmWin32OsRead(lsm_file * pFile,lsm_i64 iOff,void * pData,int nData)332 static int lsmWin32OsRead(
333   lsm_file *pFile, /* File to read from */
334   lsm_i64 iOff,    /* Offset to read from */
335   void *pData,     /* Read data into this buffer */
336   int nData        /* Bytes of data to read */
337 ){
338   Win32File *pWin32File = (Win32File *)pFile;
339   OVERLAPPED overlapped; /* The offset for ReadFile */
340   DWORD nRead = 0;       /* Bytes read using ReadFile */
341   int nRetry = 0;        /* Number of retrys */
342 
343   memset(&overlapped, 0, sizeof(OVERLAPPED));
344   overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
345   overlapped.OffsetHigh = (LONG)((iOff>>32) & 0X7FFFFFFF);
346   while( !ReadFile(pWin32File->hFile, pData, nData, &nRead, &overlapped) &&
347          GetLastError()!=ERROR_HANDLE_EOF ){
348     if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
349     return LSM_IOERR_BKPT;
350   }
351   if( nRead<(DWORD)nData ){
352     /* Unread parts of the buffer must be zero-filled */
353     memset(&((char*)pData)[nRead], 0, nData - nRead);
354   }
355   return LSM_OK;
356 }
357 
lsmWin32OsSync(lsm_file * pFile)358 static int lsmWin32OsSync(lsm_file *pFile){
359   int rc = LSM_OK;
360 
361 #ifndef LSM_NO_SYNC
362   Win32File *pWin32File = (Win32File *)pFile;
363 
364   if( pWin32File->pMap!=NULL ){
365     if( !FlushViewOfFile(pWin32File->pMap, 0) ){
366       rc = LSM_IOERR_BKPT;
367     }
368   }
369   if( rc==LSM_OK && !FlushFileBuffers(pWin32File->hFile) ){
370     rc = LSM_IOERR_BKPT;
371   }
372 #else
373   unused_parameter(pFile);
374 #endif
375 
376   return rc;
377 }
378 
lsmWin32OsSectorSize(lsm_file * pFile)379 static int lsmWin32OsSectorSize(lsm_file *pFile){
380   return 512;
381 }
382 
win32Unmap(Win32File * pWin32File)383 static void win32Unmap(Win32File *pWin32File){
384   if( pWin32File->pMap!=NULL ){
385     UnmapViewOfFile(pWin32File->pMap);
386     pWin32File->pMap = NULL;
387     pWin32File->nMap = 0;
388   }
389   if( pWin32File->hMap!=NULL ){
390     CloseHandle(pWin32File->hMap);
391     pWin32File->hMap = NULL;
392   }
393 }
394 
lsmWin32OsRemap(lsm_file * pFile,lsm_i64 iMin,void ** ppOut,lsm_i64 * pnOut)395 static int lsmWin32OsRemap(
396   lsm_file *pFile,
397   lsm_i64 iMin,
398   void **ppOut,
399   lsm_i64 *pnOut
400 ){
401   Win32File *pWin32File = (Win32File *)pFile;
402 
403   /* If the file is between 0 and 2MB in size, extend it in chunks of 256K.
404   ** Thereafter, in chunks of 1MB at a time.  */
405   const int aIncrSz[] = {256*1024, 1024*1024};
406   int nIncrSz = aIncrSz[iMin>(2*1024*1024)];
407 
408   *ppOut = NULL;
409   *pnOut = 0;
410 
411   win32Unmap(pWin32File);
412   if( iMin>=0 ){
413     LARGE_INTEGER fileSize;
414     DWORD dwSizeHigh;
415     DWORD dwSizeLow;
416     HANDLE hMap;
417     LPVOID pMap;
418     memset(&fileSize, 0, sizeof(LARGE_INTEGER));
419     if( !GetFileSizeEx(pWin32File->hFile, &fileSize) ){
420       return LSM_IOERR_BKPT;
421     }
422     assert( fileSize.QuadPart>=0 );
423     if( fileSize.QuadPart<iMin ){
424       int rc;
425       fileSize.QuadPart = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz;
426       rc = lsmWin32OsTruncate(pFile, fileSize.QuadPart);
427       if( rc!=LSM_OK ){
428         return rc;
429       }
430     }
431     dwSizeLow = (DWORD)(fileSize.QuadPart & 0xFFFFFFFF);
432     dwSizeHigh = (DWORD)((fileSize.QuadPart & 0x7FFFFFFFFFFFFFFF) >> 32);
433     hMap = CreateFileMappingW(pWin32File->hFile, NULL, PAGE_READWRITE,
434                               dwSizeHigh, dwSizeLow, NULL);
435     if( hMap==NULL ){
436       return LSM_IOERR_BKPT;
437     }
438     pWin32File->hMap = hMap;
439     assert( fileSize.QuadPart<=0xFFFFFFFF );
440     pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
441                          (SIZE_T)fileSize.QuadPart);
442     if( pMap==NULL ){
443       return LSM_IOERR_BKPT;
444     }
445     pWin32File->pMap = pMap;
446     pWin32File->nMap = (SIZE_T)fileSize.QuadPart;
447   }
448   *ppOut = pWin32File->pMap;
449   *pnOut = pWin32File->nMap;
450   return LSM_OK;
451 }
452 
win32IsDriveLetterAndColon(const char * zPathname)453 static BOOL win32IsDriveLetterAndColon(
454   const char *zPathname
455 ){
456   return ( isalpha(zPathname[0]) && zPathname[1]==':' );
457 }
458 
lsmWin32OsFullpath(lsm_env * pEnv,const char * zName,char * zOut,int * pnOut)459 static int lsmWin32OsFullpath(
460   lsm_env *pEnv,
461   const char *zName,
462   char *zOut,
463   int *pnOut
464 ){
465   DWORD nByte;
466   void *zConverted;
467   LPWSTR zTempWide;
468   char *zTempUtf8;
469 
470   if( zName[0]=='/' && win32IsDriveLetterAndColon(zName+1) ){
471     zName++;
472   }
473   zConverted = win32Utf8ToUnicode(pEnv, zName);
474   if( zConverted==0 ){
475     return LSM_NOMEM_BKPT;
476   }
477   nByte = GetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
478   if( nByte==0 ){
479     lsmFree(pEnv, zConverted);
480     return LSM_IOERR_BKPT;
481   }
482   nByte += 3;
483   zTempWide = lsmMallocZero(pEnv, nByte * sizeof(zTempWide[0]));
484   if( zTempWide==0 ){
485     lsmFree(pEnv, zConverted);
486     return LSM_NOMEM_BKPT;
487   }
488   nByte = GetFullPathNameW((LPCWSTR)zConverted, nByte, zTempWide, 0);
489   if( nByte==0 ){
490     lsmFree(pEnv, zConverted);
491     lsmFree(pEnv, zTempWide);
492     return LSM_IOERR_BKPT;
493   }
494   lsmFree(pEnv, zConverted);
495   zTempUtf8 = win32UnicodeToUtf8(pEnv, zTempWide);
496   lsmFree(pEnv, zTempWide);
497   if( zTempUtf8 ){
498     int nOut = *pnOut;
499     int nLen = strlen(zTempUtf8) + 1;
500     if( nLen<=nOut ){
501       snprintf(zOut, nOut, "%s", zTempUtf8);
502     }
503     lsmFree(pEnv, zTempUtf8);
504     *pnOut = nLen;
505     return LSM_OK;
506   }else{
507     return LSM_NOMEM_BKPT;
508   }
509 }
510 
lsmWin32OsFileid(lsm_file * pFile,void * pBuf,int * pnBuf)511 static int lsmWin32OsFileid(
512   lsm_file *pFile,
513   void *pBuf,
514   int *pnBuf
515 ){
516   int nBuf;
517   int nReq;
518   u8 *pBuf2 = (u8 *)pBuf;
519   Win32File *pWin32File = (Win32File *)pFile;
520   BY_HANDLE_FILE_INFORMATION fileInfo;
521 
522   nBuf = *pnBuf;
523   nReq = (sizeof(fileInfo.dwVolumeSerialNumber) +
524           sizeof(fileInfo.nFileIndexHigh) +
525           sizeof(fileInfo.nFileIndexLow));
526   *pnBuf = nReq;
527   if( nReq>nBuf ) return LSM_OK;
528   memset(&fileInfo, 0, sizeof(BY_HANDLE_FILE_INFORMATION));
529   if( !GetFileInformationByHandle(pWin32File->hFile, &fileInfo) ){
530     return LSM_IOERR_BKPT;
531   }
532   nReq = sizeof(fileInfo.dwVolumeSerialNumber);
533   memcpy(pBuf2, &fileInfo.dwVolumeSerialNumber, nReq);
534   pBuf2 += nReq;
535   nReq = sizeof(fileInfo.nFileIndexHigh);
536   memcpy(pBuf, &fileInfo.nFileIndexHigh, nReq);
537   pBuf2 += nReq;
538   nReq = sizeof(fileInfo.nFileIndexLow);
539   memcpy(pBuf2, &fileInfo.nFileIndexLow, nReq);
540   return LSM_OK;
541 }
542 
win32Delete(lsm_env * pEnv,const char * zFile)543 static int win32Delete(
544   lsm_env *pEnv,
545   const char *zFile
546 ){
547   int rc;
548   LPWSTR zConverted;
549 
550   zConverted = win32Utf8ToUnicode(pEnv, zFile);
551   if( zConverted==0 ){
552     rc = LSM_NOMEM_BKPT;
553   }else{
554     int nRetry = 0;
555     DWORD attr;
556 
557     do {
558       attr = GetFileAttributesW(zConverted);
559       if ( attr==INVALID_FILE_ATTRIBUTES ){
560         rc = LSM_IOERR_BKPT;
561         break;
562       }
563       if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
564         rc = LSM_IOERR_BKPT; /* Files only. */
565         break;
566       }
567       if ( DeleteFileW(zConverted) ){
568         rc = LSM_OK; /* Deleted OK. */
569         break;
570       }
571       if ( !win32RetryIoerr(pEnv, &nRetry) ){
572         rc = LSM_IOERR_BKPT; /* No more retries. */
573         break;
574       }
575     }while( 1 );
576   }
577   lsmFree(pEnv, zConverted);
578   return rc;
579 }
580 
lsmWin32OsUnlink(lsm_env * pEnv,const char * zFile)581 static int lsmWin32OsUnlink(lsm_env *pEnv, const char *zFile){
582   return win32Delete(pEnv, zFile);
583 }
584 
585 #if !defined(win32IsLockBusy)
586 #define win32IsLockBusy(a) (((a)==ERROR_LOCK_VIOLATION) || \
587                             ((a)==ERROR_IO_PENDING))
588 #endif
589 
win32LockFile(Win32File * pWin32File,int iLock,int nLock,int eType)590 static int win32LockFile(
591   Win32File *pWin32File,
592   int iLock,
593   int nLock,
594   int eType
595 ){
596   OVERLAPPED ovlp;
597 
598   assert( LSM_LOCK_UNLOCK==0 );
599   assert( LSM_LOCK_SHARED==1 );
600   assert( LSM_LOCK_EXCL==2 );
601   assert( eType>=LSM_LOCK_UNLOCK && eType<=LSM_LOCK_EXCL );
602   assert( nLock>=0 );
603   assert( iLock>0 && iLock<=32 );
604 
605   memset(&ovlp, 0, sizeof(OVERLAPPED));
606   ovlp.Offset = (4096-iLock-nLock+1);
607   if( eType>LSM_LOCK_UNLOCK ){
608     DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
609     if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK;
610     if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){
611       if( win32IsLockBusy(GetLastError()) ){
612         return LSM_BUSY;
613       }else{
614         return LSM_IOERR_BKPT;
615       }
616     }
617   }else{
618     if( !UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp) ){
619       return LSM_IOERR_BKPT;
620     }
621   }
622   return LSM_OK;
623 }
624 
lsmWin32OsLock(lsm_file * pFile,int iLock,int eType)625 static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
626   Win32File *pWin32File = (Win32File *)pFile;
627   return win32LockFile(pWin32File, iLock, 1, eType);
628 }
629 
lsmWin32OsTestLock(lsm_file * pFile,int iLock,int nLock,int eType)630 static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
631   int rc;
632   Win32File *pWin32File = (Win32File *)pFile;
633   rc = win32LockFile(pWin32File, iLock, nLock, eType);
634   if( rc!=LSM_OK ) return rc;
635   win32LockFile(pWin32File, iLock, nLock, LSM_LOCK_UNLOCK);
636   return LSM_OK;
637 }
638 
lsmWin32OsShmMap(lsm_file * pFile,int iChunk,int sz,void ** ppShm)639 static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
640   int rc;
641   Win32File *pWin32File = (Win32File *)pFile;
642   int iOffset = iChunk * sz;
643   int iOffsetShift = iOffset % pWin32File->sysInfo.dwAllocationGranularity;
644   int nNew = iChunk + 1;
645   lsm_i64 nReq = nNew * sz;
646 
647   *ppShm = NULL;
648   assert( sz>=0 );
649   assert( sz==LSM_SHM_CHUNK_SIZE );
650   if( iChunk>=pWin32File->nShm ){
651     LPHANDLE ahNew;
652     LPVOID *apNew;
653     LARGE_INTEGER fileSize;
654 
655     /* If the shared-memory file has not been opened, open it now. */
656     if( pWin32File->hShmFile==NULL ){
657       char *zShm = win32ShmFile(pWin32File);
658       if( !zShm ) return LSM_NOMEM_BKPT;
659       rc = win32Open(pWin32File->pEnv, zShm, 0, &pWin32File->hShmFile);
660       lsmFree(pWin32File->pEnv, zShm);
661       if( rc!=LSM_OK ){
662         return rc;
663       }
664     }
665 
666     /* If the shared-memory file is not large enough to contain the
667     ** requested chunk, cause it to grow.  */
668     memset(&fileSize, 0, sizeof(LARGE_INTEGER));
669     if( !GetFileSizeEx(pWin32File->hShmFile, &fileSize) ){
670       return LSM_IOERR_BKPT;
671     }
672     assert( fileSize.QuadPart>=0 );
673     if( fileSize.QuadPart<nReq ){
674       rc = win32Truncate(pWin32File->hShmFile, nReq);
675       if( rc!=LSM_OK ){
676         return rc;
677       }
678     }
679 
680     ahNew = (LPHANDLE)lsmMallocZero(pWin32File->pEnv, sizeof(HANDLE) * nNew);
681     if( !ahNew ) return LSM_NOMEM_BKPT;
682     apNew = (LPVOID *)lsmMallocZero(pWin32File->pEnv, sizeof(LPVOID) * nNew);
683     if( !apNew ){
684       lsmFree(pWin32File->pEnv, ahNew);
685       return LSM_NOMEM_BKPT;
686     }
687     memcpy(ahNew, pWin32File->ahShm, sizeof(HANDLE) * pWin32File->nShm);
688     memcpy(apNew, pWin32File->apShm, sizeof(LPVOID) * pWin32File->nShm);
689     lsmFree(pWin32File->pEnv, pWin32File->ahShm);
690     pWin32File->ahShm = ahNew;
691     lsmFree(pWin32File->pEnv, pWin32File->apShm);
692     pWin32File->apShm = apNew;
693     pWin32File->nShm = nNew;
694   }
695 
696   if( pWin32File->ahShm[iChunk]==NULL ){
697     HANDLE hMap;
698     assert( nReq<=0xFFFFFFFF );
699     hMap = CreateFileMappingW(pWin32File->hShmFile, NULL, PAGE_READWRITE, 0,
700                               (DWORD)nReq, NULL);
701     if( hMap==NULL ){
702       return LSM_IOERR_BKPT;
703     }
704     pWin32File->ahShm[iChunk] = hMap;
705   }
706   if( pWin32File->apShm[iChunk]==NULL ){
707     LPVOID pMap;
708     pMap = MapViewOfFile(pWin32File->ahShm[iChunk],
709                          FILE_MAP_WRITE | FILE_MAP_READ, 0,
710                          iOffset - iOffsetShift, sz + iOffsetShift);
711     if( pMap==NULL ){
712       return LSM_IOERR_BKPT;
713     }
714     pWin32File->apShm[iChunk] = pMap;
715   }
716   if( iOffsetShift!=0 ){
717     char *p = (char *)pWin32File->apShm[iChunk];
718     *ppShm = (void *)&p[iOffsetShift];
719   }else{
720     *ppShm = pWin32File->apShm[iChunk];
721   }
722   return LSM_OK;
723 }
724 
lsmWin32OsShmBarrier(void)725 static void lsmWin32OsShmBarrier(void){
726   MemoryBarrier();
727 }
728 
lsmWin32OsShmUnmap(lsm_file * pFile,int bDelete)729 static int lsmWin32OsShmUnmap(lsm_file *pFile, int bDelete){
730   Win32File *pWin32File = (Win32File *)pFile;
731 
732   if( pWin32File->hShmFile!=NULL ){
733     int i;
734     for(i=0; i<pWin32File->nShm; i++){
735       if( pWin32File->apShm[i]!=NULL ){
736         UnmapViewOfFile(pWin32File->apShm[i]);
737         pWin32File->apShm[i] = NULL;
738       }
739       if( pWin32File->ahShm[i]!=NULL ){
740         CloseHandle(pWin32File->ahShm[i]);
741         pWin32File->ahShm[i] = NULL;
742       }
743     }
744     CloseHandle(pWin32File->hShmFile);
745     pWin32File->hShmFile = NULL;
746     if( bDelete ){
747       char *zShm = win32ShmFile(pWin32File);
748       if( zShm ){ win32Delete(pWin32File->pEnv, zShm); }
749       lsmFree(pWin32File->pEnv, zShm);
750     }
751   }
752   return LSM_OK;
753 }
754 
755 #define MX_CLOSE_ATTEMPT 3
lsmWin32OsClose(lsm_file * pFile)756 static int lsmWin32OsClose(lsm_file *pFile){
757   int rc;
758   int nRetry = 0;
759   Win32File *pWin32File = (Win32File *)pFile;
760   lsmWin32OsShmUnmap(pFile, 0);
761   win32Unmap(pWin32File);
762   do{
763     if( pWin32File->hFile==NULL ){
764       rc = LSM_IOERR_BKPT;
765       break;
766     }
767     rc = CloseHandle(pWin32File->hFile);
768     if( rc ){
769       pWin32File->hFile = NULL;
770       rc = LSM_OK;
771       break;
772     }
773     if( ++nRetry>=MX_CLOSE_ATTEMPT ){
774       rc = LSM_IOERR_BKPT;
775       break;
776     }
777   }while( 1 );
778   lsmFree(pWin32File->pEnv, pWin32File->ahShm);
779   lsmFree(pWin32File->pEnv, pWin32File->apShm);
780   lsmFree(pWin32File->pEnv, pWin32File);
781   return rc;
782 }
783 
lsmWin32OsSleep(lsm_env * pEnv,int us)784 static int lsmWin32OsSleep(lsm_env *pEnv, int us){
785   unused_parameter(pEnv);
786   return win32Sleep(us);
787 }
788 
789 /****************************************************************************
790 ** Memory allocation routines.
791 */
792 
lsmWin32OsMalloc(lsm_env * pEnv,size_t N)793 static void *lsmWin32OsMalloc(lsm_env *pEnv, size_t N){
794   assert( HeapValidate(GetProcessHeap(), 0, NULL) );
795   return HeapAlloc(GetProcessHeap(), 0, (SIZE_T)N);
796 }
797 
lsmWin32OsFree(lsm_env * pEnv,void * p)798 static void lsmWin32OsFree(lsm_env *pEnv, void *p){
799   assert( HeapValidate(GetProcessHeap(), 0, NULL) );
800   if( p ){
801     HeapFree(GetProcessHeap(), 0, p);
802   }
803 }
804 
lsmWin32OsRealloc(lsm_env * pEnv,void * p,size_t N)805 static void *lsmWin32OsRealloc(lsm_env *pEnv, void *p, size_t N){
806   unsigned char *m = (unsigned char *)p;
807   assert( HeapValidate(GetProcessHeap(), 0, NULL) );
808   if( 1>N ){
809     lsmWin32OsFree(pEnv, p);
810     return NULL;
811   }else if( NULL==p ){
812     return lsmWin32OsMalloc(pEnv, N);
813   }else{
814 #if 0 /* arguable: don't shrink */
815     SIZE_T sz = HeapSize(GetProcessHeap(), 0, m);
816     if( sz>=(SIZE_T)N ){
817       return p;
818     }
819 #endif
820     return HeapReAlloc(GetProcessHeap(), 0, m, N);
821   }
822 }
823 
lsmWin32OsMSize(lsm_env * pEnv,void * p)824 static size_t lsmWin32OsMSize(lsm_env *pEnv, void *p){
825   assert( HeapValidate(GetProcessHeap(), 0, NULL) );
826   return (size_t)HeapSize(GetProcessHeap(), 0, p);
827 }
828 
829 
830 #ifdef LSM_MUTEX_WIN32
831 /*************************************************************************
832 ** Mutex methods for Win32 based systems.  If LSM_MUTEX_WIN32 is
833 ** missing then a no-op implementation of mutexes found below will be
834 ** used instead.
835 */
836 #include "windows.h"
837 
838 typedef struct Win32Mutex Win32Mutex;
839 struct Win32Mutex {
840   lsm_env *pEnv;
841   CRITICAL_SECTION mutex;
842 #ifdef LSM_DEBUG
843   DWORD owner;
844 #endif
845 };
846 
847 #ifndef WIN32_MUTEX_INITIALIZER
848 # define WIN32_MUTEX_INITIALIZER { 0 }
849 #endif
850 
851 #ifdef LSM_DEBUG
852 # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER, 0 }
853 #else
854 # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER }
855 #endif
856 
lsmWin32OsMutexStatic(lsm_env * pEnv,int iMutex,lsm_mutex ** ppStatic)857 static int lsmWin32OsMutexStatic(
858   lsm_env *pEnv,
859   int iMutex,
860   lsm_mutex **ppStatic
861 ){
862   static volatile LONG initialized = 0;
863   static Win32Mutex sMutex[2] = {
864     LSM_WIN32_STATIC_MUTEX,
865     LSM_WIN32_STATIC_MUTEX
866   };
867 
868   assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP );
869   assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 );
870 
871   if( InterlockedCompareExchange(&initialized, 1, 0)==0 ){
872     int i;
873     for(i=0; i<array_size(sMutex); i++){
874       InitializeCriticalSection(&sMutex[i].mutex);
875     }
876   }
877   *ppStatic = (lsm_mutex *)&sMutex[iMutex-1];
878   return LSM_OK;
879 }
880 
lsmWin32OsMutexNew(lsm_env * pEnv,lsm_mutex ** ppNew)881 static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
882   Win32Mutex *pMutex;           /* Pointer to new mutex */
883 
884   pMutex = (Win32Mutex *)lsmMallocZero(pEnv, sizeof(Win32Mutex));
885   if( !pMutex ) return LSM_NOMEM_BKPT;
886 
887   pMutex->pEnv = pEnv;
888   InitializeCriticalSection(&pMutex->mutex);
889 
890   *ppNew = (lsm_mutex *)pMutex;
891   return LSM_OK;
892 }
893 
lsmWin32OsMutexDel(lsm_mutex * p)894 static void lsmWin32OsMutexDel(lsm_mutex *p){
895   Win32Mutex *pMutex = (Win32Mutex *)p;
896   DeleteCriticalSection(&pMutex->mutex);
897   lsmFree(pMutex->pEnv, pMutex);
898 }
899 
lsmWin32OsMutexEnter(lsm_mutex * p)900 static void lsmWin32OsMutexEnter(lsm_mutex *p){
901   Win32Mutex *pMutex = (Win32Mutex *)p;
902   EnterCriticalSection(&pMutex->mutex);
903 
904 #ifdef LSM_DEBUG
905   assert( pMutex->owner!=GetCurrentThreadId() );
906   pMutex->owner = GetCurrentThreadId();
907   assert( pMutex->owner==GetCurrentThreadId() );
908 #endif
909 }
910 
lsmWin32OsMutexTry(lsm_mutex * p)911 static int lsmWin32OsMutexTry(lsm_mutex *p){
912   BOOL bRet;
913   Win32Mutex *pMutex = (Win32Mutex *)p;
914   bRet = TryEnterCriticalSection(&pMutex->mutex);
915 #ifdef LSM_DEBUG
916   if( bRet ){
917     assert( pMutex->owner!=GetCurrentThreadId() );
918     pMutex->owner = GetCurrentThreadId();
919     assert( pMutex->owner==GetCurrentThreadId() );
920   }
921 #endif
922   return !bRet;
923 }
924 
lsmWin32OsMutexLeave(lsm_mutex * p)925 static void lsmWin32OsMutexLeave(lsm_mutex *p){
926   Win32Mutex *pMutex = (Win32Mutex *)p;
927 #ifdef LSM_DEBUG
928   assert( pMutex->owner==GetCurrentThreadId() );
929   pMutex->owner = 0;
930   assert( pMutex->owner!=GetCurrentThreadId() );
931 #endif
932   LeaveCriticalSection(&pMutex->mutex);
933 }
934 
935 #ifdef LSM_DEBUG
lsmWin32OsMutexHeld(lsm_mutex * p)936 static int lsmWin32OsMutexHeld(lsm_mutex *p){
937   Win32Mutex *pMutex = (Win32Mutex *)p;
938   return pMutex ? pMutex->owner==GetCurrentThreadId() : 1;
939 }
lsmWin32OsMutexNotHeld(lsm_mutex * p)940 static int lsmWin32OsMutexNotHeld(lsm_mutex *p){
941   Win32Mutex *pMutex = (Win32Mutex *)p;
942   return pMutex ? pMutex->owner!=GetCurrentThreadId() : 1;
943 }
944 #endif
945 /*
946 ** End of Win32 mutex implementation.
947 *************************************************************************/
948 #else
949 /*************************************************************************
950 ** Noop mutex implementation
951 */
952 typedef struct NoopMutex NoopMutex;
953 struct NoopMutex {
954   lsm_env *pEnv;                  /* Environment handle (for xFree()) */
955   int bHeld;                      /* True if mutex is held */
956   int bStatic;                    /* True for a static mutex */
957 };
958 static NoopMutex aStaticNoopMutex[2] = {
959   {0, 0, 1},
960   {0, 0, 1},
961 };
962 
lsmWin32OsMutexStatic(lsm_env * pEnv,int iMutex,lsm_mutex ** ppStatic)963 static int lsmWin32OsMutexStatic(
964   lsm_env *pEnv,
965   int iMutex,
966   lsm_mutex **ppStatic
967 ){
968   assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) );
969   *ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1];
970   return LSM_OK;
971 }
lsmWin32OsMutexNew(lsm_env * pEnv,lsm_mutex ** ppNew)972 static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
973   NoopMutex *p;
974   p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex));
975   if( p ) p->pEnv = pEnv;
976   *ppNew = (lsm_mutex *)p;
977   return (p ? LSM_OK : LSM_NOMEM_BKPT);
978 }
lsmWin32OsMutexDel(lsm_mutex * pMutex)979 static void lsmWin32OsMutexDel(lsm_mutex *pMutex)  {
980   NoopMutex *p = (NoopMutex *)pMutex;
981   assert( p->bStatic==0 && p->pEnv );
982   lsmFree(p->pEnv, p);
983 }
lsmWin32OsMutexEnter(lsm_mutex * pMutex)984 static void lsmWin32OsMutexEnter(lsm_mutex *pMutex){
985   NoopMutex *p = (NoopMutex *)pMutex;
986   assert( p->bHeld==0 );
987   p->bHeld = 1;
988 }
lsmWin32OsMutexTry(lsm_mutex * pMutex)989 static int lsmWin32OsMutexTry(lsm_mutex *pMutex){
990   NoopMutex *p = (NoopMutex *)pMutex;
991   assert( p->bHeld==0 );
992   p->bHeld = 1;
993   return 0;
994 }
lsmWin32OsMutexLeave(lsm_mutex * pMutex)995 static void lsmWin32OsMutexLeave(lsm_mutex *pMutex){
996   NoopMutex *p = (NoopMutex *)pMutex;
997   assert( p->bHeld==1 );
998   p->bHeld = 0;
999 }
1000 #ifdef LSM_DEBUG
lsmWin32OsMutexHeld(lsm_mutex * pMutex)1001 static int lsmWin32OsMutexHeld(lsm_mutex *pMutex){
1002   NoopMutex *p = (NoopMutex *)pMutex;
1003   return p ? p->bHeld : 1;
1004 }
lsmWin32OsMutexNotHeld(lsm_mutex * pMutex)1005 static int lsmWin32OsMutexNotHeld(lsm_mutex *pMutex){
1006   NoopMutex *p = (NoopMutex *)pMutex;
1007   return p ? !p->bHeld : 1;
1008 }
1009 #endif
1010 /***************************************************************************/
1011 #endif /* else LSM_MUTEX_NONE */
1012 
1013 /* Without LSM_DEBUG, the MutexHeld tests are never called */
1014 #ifndef LSM_DEBUG
1015 # define lsmWin32OsMutexHeld    0
1016 # define lsmWin32OsMutexNotHeld 0
1017 #endif
1018 
lsm_default_env(void)1019 lsm_env *lsm_default_env(void){
1020   static lsm_env win32_env = {
1021     sizeof(lsm_env),         /* nByte */
1022     1,                       /* iVersion */
1023     /***** file i/o ******************/
1024     0,                       /* pVfsCtx */
1025     lsmWin32OsFullpath,      /* xFullpath */
1026     lsmWin32OsOpen,          /* xOpen */
1027     lsmWin32OsRead,          /* xRead */
1028     lsmWin32OsWrite,         /* xWrite */
1029     lsmWin32OsTruncate,      /* xTruncate */
1030     lsmWin32OsSync,          /* xSync */
1031     lsmWin32OsSectorSize,    /* xSectorSize */
1032     lsmWin32OsRemap,         /* xRemap */
1033     lsmWin32OsFileid,        /* xFileid */
1034     lsmWin32OsClose,         /* xClose */
1035     lsmWin32OsUnlink,        /* xUnlink */
1036     lsmWin32OsLock,          /* xLock */
1037     lsmWin32OsTestLock,      /* xTestLock */
1038     lsmWin32OsShmMap,        /* xShmMap */
1039     lsmWin32OsShmBarrier,    /* xShmBarrier */
1040     lsmWin32OsShmUnmap,      /* xShmUnmap */
1041     /***** memory allocation *********/
1042     0,                       /* pMemCtx */
1043     lsmWin32OsMalloc,        /* xMalloc */
1044     lsmWin32OsRealloc,       /* xRealloc */
1045     lsmWin32OsFree,          /* xFree */
1046     lsmWin32OsMSize,         /* xSize */
1047     /***** mutexes *********************/
1048     0,                       /* pMutexCtx */
1049     lsmWin32OsMutexStatic,   /* xMutexStatic */
1050     lsmWin32OsMutexNew,      /* xMutexNew */
1051     lsmWin32OsMutexDel,      /* xMutexDel */
1052     lsmWin32OsMutexEnter,    /* xMutexEnter */
1053     lsmWin32OsMutexTry,      /* xMutexTry */
1054     lsmWin32OsMutexLeave,    /* xMutexLeave */
1055     lsmWin32OsMutexHeld,     /* xMutexHeld */
1056     lsmWin32OsMutexNotHeld,  /* xMutexNotHeld */
1057     /***** other *********************/
1058     lsmWin32OsSleep,         /* xSleep */
1059   };
1060   return &win32_env;
1061 }
1062 
1063 #endif
1064