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; }