/ Check-in [0207fd9b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge the improved anti-virus defenses into the trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0207fd9b0c0f6baa3c2cb8ba588ad585507848b3
User & Date: drh 2011-07-12 14:38:25
Context
2011-07-13
16:03
Added the SQLITE_FCNTL_WIN32_AV_RETRY file control for configuring the retry counts and delays in the windows VFS. check-in: 7aaf0a6a user: drh tags: trunk
2011-07-12
14:38
Merge the improved anti-virus defenses into the trunk. check-in: 0207fd9b user: drh tags: trunk
14:02
Revise logic in winDelete to check the file prior to attempting to delete it. Closed-Leaf check-in: 36f11acc user: mistachkin tags: av-defense
2011-07-11
23:45
Update the TCL commands for setting windows manditory locks. Add test cases for manditory lock delays under windows. check-in: 03af4c17 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_win.c.

   399    399         iLine, iErrno, zFunc, zPath, zMsg
   400    400     );
   401    401   
   402    402     return errcode;
   403    403   }
   404    404   
   405    405   /*
   406         -** The number of times that a ReadFile() or WriteFile() will be retried
   407         -** following a locking error.
          406  +** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
          407  +** will be retried following a locking error - probably caused by 
          408  +** antivirus software.  Also the initial delay before the first retry.
          409  +** The delay increases linearly with each retry.
   408    410   */
   409    411   #ifndef SQLITE_WIN32_IOERR_RETRY
   410         -# define SQLITE_WIN32_IOERR_RETRY 5
          412  +# define SQLITE_WIN32_IOERR_RETRY 10
          413  +#endif
          414  +#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
          415  +# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
   411    416   #endif
   412    417   
   413    418   /*
   414    419   ** If a ReadFile() or WriteFile() error occurs, invoke this routine
   415    420   ** to see if it should be retried.  Return TRUE to retry.  Return FALSE
   416    421   ** to give up with an error.
   417    422   */
   418    423   static int retryIoerr(int *pnRetry){
   419    424     DWORD e;
   420    425     if( *pnRetry>=SQLITE_WIN32_IOERR_RETRY ){
   421    426       return 0;
   422    427     }
   423    428     e = GetLastError();
   424         -  if( e==ERROR_LOCK_VIOLATION || e==ERROR_SHARING_VIOLATION ){
   425         -    Sleep(50 + 50*(*pnRetry));
          429  +  if( e==ERROR_ACCESS_DENIED ||
          430  +      e==ERROR_LOCK_VIOLATION ||
          431  +      e==ERROR_SHARING_VIOLATION ){
          432  +    Sleep(SQLITE_WIN32_IOERR_RETRY_DELAY*(1+*pnRetry));
   426    433       ++*pnRetry;
   427    434       return 1;
   428    435     }
   429    436     return 0;
   430    437   }
          438  +
          439  +/*
          440  +** Log a I/O error retry episode.
          441  +*/
          442  +static void logIoerr(int nRetry){
          443  +  if( nRetry ){
          444  +    sqlite3_log(SQLITE_IOERR, 
          445  +      "delayed %dms for lock/sharing conflict",
          446  +      SQLITE_WIN32_IOERR_RETRY_DELAY*nRetry*(nRetry+1)/2
          447  +    );
          448  +  }
          449  +}
   431    450   
   432    451   #if SQLITE_OS_WINCE
   433    452   /*************************************************************************
   434    453   ** This section contains code for WinCE only.
   435    454   */
   436    455   /*
   437    456   ** WindowsCE does not have a localtime() function.  So create a
................................................................................
   857    876       return SQLITE_FULL;
   858    877     }
   859    878     while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
   860    879       if( retryIoerr(&nRetry) ) continue;
   861    880       pFile->lastErrno = GetLastError();
   862    881       return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
   863    882     }
          883  +  logIoerr(nRetry);
   864    884     if( nRead<(DWORD)amt ){
   865    885       /* Unread parts of the buffer must be zero-filled */
   866    886       memset(&((char*)pBuf)[nRead], 0, amt-nRead);
   867    887       return SQLITE_IOERR_SHORT_READ;
   868    888     }
   869    889   
   870    890     return SQLITE_OK;
................................................................................
   878    898     sqlite3_file *id,               /* File to write into */
   879    899     const void *pBuf,               /* The bytes to be written */
   880    900     int amt,                        /* Number of bytes to write */
   881    901     sqlite3_int64 offset            /* Offset into the file to begin writing at */
   882    902   ){
   883    903     int rc;                         /* True if error has occured, else false */
   884    904     winFile *pFile = (winFile*)id;  /* File handle */
          905  +  int nRetry = 0;                 /* Number of retries */
   885    906   
   886    907     assert( amt>0 );
   887    908     assert( pFile );
   888    909     SimulateIOError(return SQLITE_IOERR_WRITE);
   889    910     SimulateDiskfullError(return SQLITE_FULL);
   890    911   
   891    912     OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
   892    913   
   893    914     rc = seekWinFile(pFile, offset);
   894    915     if( rc==0 ){
   895    916       u8 *aRem = (u8 *)pBuf;        /* Data yet to be written */
   896    917       int nRem = amt;               /* Number of bytes yet to be written */
   897    918       DWORD nWrite;                 /* Bytes written by each WriteFile() call */
   898         -    int nRetry = 0;               /* Number of retries */
   899    919   
   900    920       while( nRem>0 ){
   901    921         if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
   902    922           if( retryIoerr(&nRetry) ) continue;
   903    923           break;
   904    924         }
   905    925         if( nWrite<=0 ) break;
................................................................................
   914    934   
   915    935     if( rc ){
   916    936       if(   ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
   917    937          || ( pFile->lastErrno==ERROR_DISK_FULL )){
   918    938         return SQLITE_FULL;
   919    939       }
   920    940       return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
          941  +  }else{
          942  +    logIoerr(nRetry);
   921    943     }
   922    944     return SQLITE_OK;
   923    945   }
   924    946   
   925    947   /*
   926    948   ** Truncate an open file to a specified size
   927    949   */
................................................................................
  2347   2369   ** will open a journal file shortly after it is created in order to do
  2348   2370   ** whatever it does.  While this other process is holding the
  2349   2371   ** file open, we will be unable to delete it.  To work around this
  2350   2372   ** problem, we delay 100 milliseconds and try to delete again.  Up
  2351   2373   ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
  2352   2374   ** up and returning an error.
  2353   2375   */
  2354         -#define MX_DELETION_ATTEMPTS 5
  2355   2376   static int winDelete(
  2356   2377     sqlite3_vfs *pVfs,          /* Not used on win32 */
  2357   2378     const char *zFilename,      /* Name of file to delete */
  2358   2379     int syncDir                 /* Not used on win32 */
  2359   2380   ){
  2360   2381     int cnt = 0;
  2361         -  DWORD rc;
  2362         -  DWORD error = 0;
         2382  +  int rc;
  2363   2383     void *zConverted;
  2364   2384     UNUSED_PARAMETER(pVfs);
  2365   2385     UNUSED_PARAMETER(syncDir);
  2366   2386   
  2367   2387     SimulateIOError(return SQLITE_IOERR_DELETE);
  2368   2388     zConverted = convertUtf8Filename(zFilename);
  2369   2389     if( zConverted==0 ){
  2370   2390       return SQLITE_NOMEM;
  2371   2391     }
  2372   2392     if( isNT() ){
  2373         -    do{
  2374         -      DeleteFileW(zConverted);
  2375         -    }while(   (   ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
  2376         -               || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
  2377         -           && (++cnt < MX_DELETION_ATTEMPTS)
  2378         -           && (Sleep(100), 1) );
         2393  +    rc = 1;
         2394  +    while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
         2395  +           (rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
         2396  +    rc = rc ? SQLITE_OK : SQLITE_ERROR;
  2379   2397   /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. 
  2380   2398   ** Since the ASCII version of these Windows API do not exist for WINCE,
  2381   2399   ** it's important to not reference them for WINCE builds.
  2382   2400   */
  2383   2401   #if SQLITE_OS_WINCE==0
  2384   2402     }else{
  2385         -    do{
  2386         -      DeleteFileA(zConverted);
  2387         -    }while(   (   ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
  2388         -               || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
  2389         -           && (++cnt < MX_DELETION_ATTEMPTS)
  2390         -           && (Sleep(100), 1) );
         2403  +    rc = 1;
         2404  +    while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
         2405  +           (rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
         2406  +    rc = rc ? SQLITE_OK : SQLITE_ERROR;
  2391   2407   #endif
  2392   2408     }
         2409  +  if( rc ){
         2410  +    rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
         2411  +  }else{
         2412  +    logIoerr(cnt);
         2413  +  }
  2393   2414     free(zConverted);
  2394         -  OSTRACE(("DELETE \"%s\" %s\n", zFilename,
  2395         -       ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
  2396         -         "ok" : "failed" ));
  2397         - 
  2398         -  return (   (rc == INVALID_FILE_ATTRIBUTES) 
  2399         -          && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK :
  2400         -                 winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
         2415  +  OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" )));
         2416  +  return rc;
  2401   2417   }
  2402   2418   
  2403   2419   /*
  2404   2420   ** Check the existance and status of a file.
  2405   2421   */
  2406   2422   static int winAccess(
  2407   2423     sqlite3_vfs *pVfs,         /* Not used on win32 */

Changes to test/wal6.test.

    39     39   	    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
    40     40   	    INSERT INTO t1 VALUES(1,2);
    41     41   	    SELECT * FROM t1;
    42     42   	  }
    43     43   	} {1 2}
    44     44   
    45     45   # Under Windows, you'll get an error trying to delete
    46         -# a file this is already opened.  For now, make sure 
    47         -# we get that error, then close the first connection
           46  +# a file this is already opened.  Close the first connection
    48     47   # so the other tests work.
    49     48   if {$tcl_platform(platform)=="windows"} {
    50     49     if {$jmode=="persist" || $jmode=="truncate"} {
    51         -	  do_test wal6-1.2.$jmode.win {
    52         -	    sqlite3 db2 test.db
    53         -	    catchsql {
    54         -		    PRAGMA journal_mode=WAL;
    55         -	    } db2
    56         -	  } {1 {disk I/O error}}
    57         -  	db2 close
    58         -	  db close
           50  +    db close
    59     51     }
    60     52   }
    61     53   
    62     54   	do_test wal6-1.2.$jmode {
    63     55   	  sqlite3 db2 test.db
    64     56   	  execsql {
    65     57   		PRAGMA journal_mode=WAL;
................................................................................
    83     75   	db close
    84     76   	db2 close
    85     77     forcedelete test.db
    86     78   
    87     79   }
    88     80   
    89     81   finish_test
    90         -

Changes to test/win32lock.test.

    16     16   if {$tcl_platform(platform)!="windows"} return
    17     17   
    18     18   set testdir [file dirname $argv0]
    19     19   source $testdir/tester.tcl
    20     20   
    21     21   set testprefix win32lock
    22     22   
           23  +db close
           24  +sqlite3_shutdown
           25  +test_sqlite3_log xLog
           26  +proc xLog {error_code msg} {
           27  +  lappend ::log $msg 
           28  +}
           29  +sqlite3 db test.db
           30  +
    23     31   do_test win32lock-1.1 {
    24     32     db eval {
    25     33       PRAGMA cache_size=10;
    26     34       CREATE TABLE t1(x,y);
    27     35       INSERT INTO t1 VALUES(1,randomblob(100000));
    28     36       INSERT INTO t1 VALUES(2,randomblob(50000));
    29     37       INSERT INTO t1 VALUES(3,randomblob(25000));
................................................................................
    45     53          set ::msg
    46     54       } {disk I/O error}
    47     55       break
    48     56     } else {
    49     57       do_test win32lock-1.2-$delay1 {
    50     58          set ::msg
    51     59       } {1 100000 2 50000 3 25000 4 12500}
           60  +    if {$::log!=""} {
           61  +      do_test win32lock-1.2-$delay1-log1 {
           62  +        regsub {\d+} $::log # x
           63  +        set x
           64  +      } {{delayed #ms for lock/sharing conflict}}
           65  +    }
    52     66       incr delay1 50
    53     67     }
           68  +  set ::log {}
    54     69   }
    55     70   sqlite3_test_control_pending_byte $old_pending_byte
    56         -
           71  +sqlite3_shutdown
           72  +test_sqlite3_log 
           73  +sqlite3_initialize
    57     74   finish_test