ales at elxala.de wrote:
> Hi, this is actually a Ticket, the good new is that is already 
> pre-analyzed
> and probably also the cause has been found.
> I hope this is the right place to put this
>
> TITLE: Wrong ERROR_DISK_FULL writing a blob on Windows
>
> DESCRIPTION:
>
>    Having a sqlite database about 4.1 GB
>    writing blobs in some table, for instance using a statement like
>       INSERT INTO inodeCargo VALUES (1000, readfile ("ScreenShot.png"));
>
>    an error ERROR_DISK_FULL can be through wrongly (under certain 
> conditions)
>
>    Windows 7 64-bit
>
> REPRODUCIBLE:
>    When the conditions are met, always reproducible
>
>
> CAUSE :
>
>    windows function WriteFile return error 112 (ERROR_DISK_FULL) when 
> trying to write
>    1024 bytes using a offset = 4294966272 = 0xFFFFFC00 = FFFFFFFF - 2023
>
>    This was observed using the default mode (OVERLAPPED) although a 
> test compiling
>    the code with
>       #define SQLITE_WIN32_NO_OVERLAPPED
>    at the begining also failed.
>
> PATCH:
>
>    The patch (see sqlite3_modified.c) work in my case, but obviously 
> cannot
>    fix the mode NO_OVERLAPPED (it does not compile in this mode). Also 
> all procedures
>    using the buggy function (if this result to be the cause) should be 
> reviewed.
>    A more precise fix has to be developed by some expert in sqlite code.
>
> ANALYSIS:
>
>    A sqlite db used to store files came in the conditions described
>    size of database file : 4.194.303 KB
>    size of journal file : 8 KB
>    size of the image (png file) : 4.417 KB
>
>    SQL Query returning systematically ERROR_DISK_FULL:
>       INSERT INTO inodeCargo VALUES (-24, 4522876, 0, readfile 
> ("ScreenShot320.png"));
>
>    (hard disk of the test with about 1 TB free space)
>
>    Since the case was 100% reproducible I could debug the problem using
>    sqlite-amalgamation-3080803.zip (ver 3.8.8.3)
>    I tried sqlite-amalgamation-201503091040.zip as well with the same
>    result, so the problem was also not fixed in this pre-release.
>
>    In debug session I found that the error came from
>
>       static int winWrite (...
>
>    and it is given in the line
>
>       if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
>
>    which is a call to a windows api function WriteFile
>
>    adding the extra debug messages into a file (see sqlite3_modified.c 
> function "static int winWrite")
>    this was the output before the fail (ERROR_DISK_FULL)
>
> ...
> WRITE file=00000030, buffer=00EDBE08, amount=1024, offset=4294965248, 
> lock=4
> OVERLAPPED offset=4294965248, OffsetHigh=0
> WRITE file=00000030, buffer=00EDC2E0, amount=1024, offset=4294966272, 
> lock=4
> OVERLAPPED offset=4294966272, OffsetHigh=0
>
>    Now note that
>
>       4294966272 = 0xFFFFFC00
>       and FFFFFFFF - FFFFFC00 = 3FF = 1023 !!!!! and we want to write 
> 1024 bytes!!!
>
>
> That this is a Microsoft bug is only a guess for what I have seen 
> debugging the code,
> anyway if not I hope this analysis helps to find it the final cause of 
> the problem and it could be fixed.
>
> Regards
> Alejandro
>
>
>
>
>
>
>
> _______________________________________________
> sqlite-users mailing list
> sqlite-users at mailinglists.sqlite.org
> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
In the previous email (from another account of mine) I did attach a 
zipped sqlite3_modified.c that
did not appear in the email published, maybe because of its size about 
1MB although it was 7zipped
anyway, now I attach the code of only function modified in sqlite3.c 
amalgamation: winWrite
here the relevant code where the patch lies


#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
     memset(&overlapped, 0, sizeof(OVERLAPPED));
     overlapped.Offset = (LONG)(offset & 0xffffffff);
     overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);

    // PATCH  !! ------------
    //
     if (((LONG)0xffffffff - (LONG)overlapped.Offset) < nRem)
       nRem = ((LONG)0xffffffff - (LONG)overlapped.Offset);
    // -----------


    // EXTRA DEBUG ------------
    //
    FILE * fout = fopen("salgo.txt", "at");
     if (fout != 0)
     {
         fprintf(fout, "OVERLAPPED offset=%u, OffsetHigh=%u\n", 
overlapped.Offset, overlapped.OffsetHigh);
         fclose(fout);
     }
    // -----------

#endif

-------------- next part --------------
/*
** Write data from a buffer into a file.  Return SQLITE_OK on success
** or some other error code on failure.
*/
static int winWrite(
  sqlite3_file *id,               /* File to write into */
  const void *pBuf,               /* The bytes to be written */
  int amt,                        /* Number of bytes to write */
  sqlite3_int64 offset            /* Offset into the file to begin writing at */
){
  int rc = 0;                     /* True if error has occurred, else false */
  winFile *pFile = (winFile*)id;  /* File handle */
  int nRetry = 0;                 /* Number of retries */

  assert( amt>0 );
  assert( pFile );
  SimulateIOError(return SQLITE_IOERR_WRITE);
  SimulateDiskfullError(return SQLITE_FULL);

  // EXTRA DEBUG ------------
  //
  FILE * fout = fopen("salgo.txt", "at");
  if (fout != 0)
  {
          fprintf(fout, "WRITE file=%p, buffer=%p, amount=%d, offset=%u, 
lock=%d\n", pFile->h, pBuf, amt, offset, pFile->locktype);
          fclose(fout);
  }
  // ------------

  OSTRACE(("WRITE file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n",
           pFile->h, pBuf, amt, offset, pFile->locktype));

#if SQLITE_MAX_MMAP_SIZE>0
  /* Deal with as much of this write request as possible by transfering
  ** data from the memory mapping using memcpy().  */
  if ((sqlite3_int64) offset < pFile->mmapSize )
  {
    if ((sqlite3_int64) offset+amt <= pFile->mmapSize )
    {
      memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
      OSTRACE(("WRITE-MMAP file=%p, rc=SQLITE_OK\n", pFile->h));
      return SQLITE_OK;
    }else{
      int nCopy = (int)(pFile->mmapSize - offset);
      memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
      pBuf = &((u8 *)pBuf)[nCopy];
      amt -= nCopy;
      offset += nCopy;
    }
  }
#endif

#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
  rc = winSeekFile(pFile, offset);
  if( rc==0 ){
#else
  {
#endif
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
    OVERLAPPED overlapped;        /* The offset for WriteFile. */
#endif
    u8 *aRem = (u8 *)pBuf;        /* Data yet to be written */
    int nRem = amt;               /* Number of bytes yet to be written */
    DWORD nWrite;                 /* Bytes written by each WriteFile() call */
    DWORD lastErrno = NO_ERROR;   /* Value returned by GetLastError() */

#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
    memset(&overlapped, 0, sizeof(OVERLAPPED));
    overlapped.Offset = (LONG)(offset & 0xffffffff);
    overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);

   // PATCH  !! ------------
   //
        if (((LONG)0xffffffff - (LONG)overlapped.Offset) < nRem)
      nRem = ((LONG)0xffffffff - (LONG)overlapped.Offset);
   // -----------


   // EXTRA DEBUG ------------
   //
   FILE * fout = fopen("salgo.txt", "at");
        if (fout != 0)
        {
                fprintf(fout, "OVERLAPPED offset=%u, OffsetHigh=%u\n", 
overlapped.Offset, overlapped.OffsetHigh);
                fclose(fout);
        }
   // -----------

#endif


    while( nRem>0 ){
#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
      if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
#else
      if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
#endif
        if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
        break;
      }
      assert( nWrite==0 || nWrite<=(DWORD)nRem );
      if( nWrite==0 || nWrite>(DWORD)nRem ){
        lastErrno = osGetLastError();
        break;
      }
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
          makrombo_wrote += nWrite;
          makrombo_nr++;
      offset += nWrite;
      overlapped.Offset = (LONG)(offset & 0xffffffff);
      overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
#endif
      aRem += nWrite;
      nRem -= nWrite;
    }
    if( nRem>0 ){
      pFile->lastErrno = lastErrno;
      rc = 1;
    }
  }

  if( rc ){
    if(   ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
       || ( pFile->lastErrno==ERROR_DISK_FULL )){
      OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h));
      return winLogError(SQLITE_FULL, pFile->lastErrno,
                         "winWrite1", pFile->zPath);
    }
    OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h));
    return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
                       "winWrite2", pFile->zPath);
  }else{
    winLogIoerr(nRetry);
  }
  OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h));
  return SQLITE_OK;
}

Reply via email to