SQLite

Check-in [89f1848d]
Login

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

Overview
Comment:Update the anti-virus retry logic for DeleteFile(). Invoke sqlite3_log() for each anti-virus retry. Make the retry delay configurable at compile-time.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | av-defense
Files: files | file ages | folders
SHA1: 89f1848d7f7d98b4f7da9218f99ed90d22dd43a8
User & Date: drh 2011-07-12 11:04:18
Original Comment: Update the anti-virus retry logic for DeleteFile(). Invoke sqlite3_log() for each anti-virus retry. Make the retry delay configurable at compile-time.
Context
2011-07-12
13:51
Improvements to the logging that occurs on an antivirus I/O retry. (check-in: ff0ff75c user: drh tags: av-defense)
11:04
Update the anti-virus retry logic for DeleteFile(). Invoke sqlite3_log() for each anti-virus retry. Make the retry delay configurable at compile-time. (check-in: 89f1848d user: drh 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
Unified Diff Ignore Whitespace Patch
Changes to src/os_win.c.
399
400
401
402
403
404
405
406
407


408
409
410



411
412
413
414
415
416
417
418
419
420
421
422
423
424



425
426
427
428
429
430
431
432
      iLine, iErrno, zFunc, zPath, zMsg
  );

  return errcode;
}

/*
** The number of times that a ReadFile() or WriteFile() will be retried
** following a locking error.


*/
#ifndef SQLITE_WIN32_IOERR_RETRY
# define SQLITE_WIN32_IOERR_RETRY 5



#endif

/*
** If a ReadFile() or WriteFile() error occurs, invoke this routine
** to see if it should be retried.  Return TRUE to retry.  Return FALSE
** to give up with an error.
*/
static int retryIoerr(int *pnRetry){
  DWORD e;
  if( *pnRetry>=SQLITE_WIN32_IOERR_RETRY ){
    return 0;
  }
  e = GetLastError();
  if( e==ERROR_LOCK_VIOLATION || e==ERROR_SHARING_VIOLATION ){



    Sleep(50 + 50*(*pnRetry));
    ++*pnRetry;
    return 1;
  }
  return 0;
}

#if SQLITE_OS_WINCE







|
|
>
>


|
>
>
>














>
>
>
|







399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
      iLine, iErrno, zFunc, zPath, zMsg
  );

  return errcode;
}

/*
** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
** will be retried following a locking error - probably caused by 
** antivirus software.  Also the initial delay before the first retry.
** The delay increases linearly with each retry.
*/
#ifndef SQLITE_WIN32_IOERR_RETRY
# define SQLITE_WIN32_IOERR_RETRY 10
#endif
#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
#endif

/*
** If a ReadFile() or WriteFile() error occurs, invoke this routine
** to see if it should be retried.  Return TRUE to retry.  Return FALSE
** to give up with an error.
*/
static int retryIoerr(int *pnRetry){
  DWORD e;
  if( *pnRetry>=SQLITE_WIN32_IOERR_RETRY ){
    return 0;
  }
  e = GetLastError();
  if( e==ERROR_LOCK_VIOLATION || e==ERROR_SHARING_VIOLATION ){
    int delay = SQLITE_WIN32_IOERR_RETRY_DELAY*(1+*pnRetry);
    sqlite3_log(SQLITE_IOERR, "delay %dms for lock/sharing violation - "
                              "probably due to antivirus software", delay);
    Sleep(delay);
    ++*pnRetry;
    return 1;
  }
  return 0;
}

#if SQLITE_OS_WINCE
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392

2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
** will open a journal file shortly after it is created in order to do
** whatever it does.  While this other process is holding the
** file open, we will be unable to delete it.  To work around this
** problem, we delay 100 milliseconds and try to delete again.  Up
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
** up and returning an error.
*/
#define MX_DELETION_ATTEMPTS 5
static int winDelete(
  sqlite3_vfs *pVfs,          /* Not used on win32 */
  const char *zFilename,      /* Name of file to delete */
  int syncDir                 /* Not used on win32 */
){
  int cnt = 0;
  DWORD rc;
  DWORD error = 0;
  void *zConverted;
  UNUSED_PARAMETER(pVfs);
  UNUSED_PARAMETER(syncDir);

  SimulateIOError(return SQLITE_IOERR_DELETE);
  zConverted = convertUtf8Filename(zFilename);
  if( zConverted==0 ){
    return SQLITE_NOMEM;
  }
  if( isNT() ){
    do{
      DeleteFileW(zConverted);
    }while(   (   ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
               || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
           && (++cnt < MX_DELETION_ATTEMPTS)
           && (Sleep(100), 1) );
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. 
** Since the ASCII version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
*/
#if SQLITE_OS_WINCE==0
  }else{
    do{
      DeleteFileA(zConverted);
    }while(   (   ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
               || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
           && (++cnt < MX_DELETION_ATTEMPTS)
           && (Sleep(100), 1) );
#endif
  }

  free(zConverted);
  OSTRACE(("DELETE \"%s\" %s\n", zFilename,
       ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
         "ok" : "failed" ));
 
  return (   (rc == INVALID_FILE_ATTRIBUTES) 
          && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK :
                 winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
}

/*
** Check the existance and status of a file.
*/
static int winAccess(
  sqlite3_vfs *pVfs,         /* Not used on win32 */







<






|
<










<
|
<
<
<
<






<
|
<
<
<
<


>

|
<
<
<
|
<
<







2355
2356
2357
2358
2359
2360
2361

2362
2363
2364
2365
2366
2367
2368

2369
2370
2371
2372
2373
2374
2375
2376
2377
2378

2379




2380
2381
2382
2383
2384
2385

2386




2387
2388
2389
2390
2391



2392


2393
2394
2395
2396
2397
2398
2399
** will open a journal file shortly after it is created in order to do
** whatever it does.  While this other process is holding the
** file open, we will be unable to delete it.  To work around this
** problem, we delay 100 milliseconds and try to delete again.  Up
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
** up and returning an error.
*/

static int winDelete(
  sqlite3_vfs *pVfs,          /* Not used on win32 */
  const char *zFilename,      /* Name of file to delete */
  int syncDir                 /* Not used on win32 */
){
  int cnt = 0;
  int rc;

  void *zConverted;
  UNUSED_PARAMETER(pVfs);
  UNUSED_PARAMETER(syncDir);

  SimulateIOError(return SQLITE_IOERR_DELETE);
  zConverted = convertUtf8Filename(zFilename);
  if( zConverted==0 ){
    return SQLITE_NOMEM;
  }
  if( isNT() ){

    while( (rc = DeleteFileW(zConverted))!=0 || retryIoerr(&cnt) ){}




/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. 
** Since the ASCII version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
*/
#if SQLITE_OS_WINCE==0
  }else{

    while( (rc = DeleteFileW(zConverted))!=0 || retryIoerr(&cnt) ){}




#endif
  }
  if( rc ) rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
  free(zConverted);
  OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" )));



  return rc;


}

/*
** Check the existance and status of a file.
*/
static int winAccess(
  sqlite3_vfs *pVfs,         /* Not used on win32 */
Changes to test/wal6.test.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
	    INSERT INTO t1 VALUES(1,2);
	    SELECT * FROM t1;
	  }
	} {1 2}

# Under Windows, you'll get an error trying to delete
# a file this is already opened.  For now, make sure 
# we get that error, then close the first connection
# so the other tests work.
if {$tcl_platform(platform)=="windows"} {
  if {$jmode=="persist" || $jmode=="truncate"} {
	  do_test wal6-1.2.$jmode.win {
	    sqlite3 db2 test.db
	    catchsql {
		    PRAGMA journal_mode=WAL;
	    } db2
	  } {1 {disk I/O error}}
  	db2 close
	  db close
  }
}

	do_test wal6-1.2.$jmode {
	  sqlite3 db2 test.db
	  execsql {
		PRAGMA journal_mode=WAL;







|
<



<
<
<
<
<
<
<
|







39
40
41
42
43
44
45
46

47
48
49







50
51
52
53
54
55
56
57
	    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
	    INSERT INTO t1 VALUES(1,2);
	    SELECT * FROM t1;
	  }
	} {1 2}

# Under Windows, you'll get an error trying to delete
# a file this is already opened.  Close the first connection

# so the other tests work.
if {$tcl_platform(platform)=="windows"} {
  if {$jmode=="persist" || $jmode=="truncate"} {







    db close
  }
}

	do_test wal6-1.2.$jmode {
	  sqlite3 db2 test.db
	  execsql {
		PRAGMA journal_mode=WAL;
83
84
85
86
87
88
89
90
	db close
	db2 close
  forcedelete test.db

}

finish_test








<
75
76
77
78
79
80
81

	db close
	db2 close
  forcedelete test.db

}

finish_test