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