1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Fix appendvfs bug exposed with bigger files, and add tests for such conditions.

FossilOrigin-Name: 19b1f53a1c0a14440ae8ac71660a2595d37a4a5b201055c19366c7dca75d6660
This commit is contained in:
larrybr
2021-03-16 06:41:51 +00:00
parent 21f0f694ff
commit 5cad178b86
4 changed files with 107 additions and 40 deletions

View File

@ -122,6 +122,10 @@ struct ApndFile {
sqlite3_int64 iPgOne;
/* File offset of written append-mark, or -1 if unwritten */
sqlite3_int64 iMark;
/* Whenever file is open and .iMark >= 0, and file size changes are
* not in progress, .iMark + sizeof(append mark) == base file size,
* and append file size == .iMark - .iPgOne .
*/
};
/*
@ -217,6 +221,7 @@ static const sqlite3_io_methods apnd_io_methods = {
** Close an apnd-file.
*/
static int apndClose(sqlite3_file *pFile){
assert((ApndFile *pFile)pFile == ORIGFILE(pFile));
pFile = ORIGFILE(pFile);
return pFile->pMethods->xClose(pFile);
}
@ -250,6 +255,7 @@ static int apndWriteMark(
int i = APND_MARK_FOS_SZ;
int rc;
assert(pFile == ORIGFILE(paf));
/* assert(pFile == paf->base); */
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
while (--i >= 0) {
a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
@ -276,6 +282,7 @@ static int apndWrite(
sqlite_int64 iWriteEnd = iOfst + iAmt;
if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
pFile = ORIGFILE(pFile);
/* assert(pFile == paf->base); */
/* If append-mark is absent or will be overwritten, write it. */
if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
int rc = apndWriteMark(paf, pFile, iWriteEnd);
@ -499,55 +506,60 @@ static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
** Open an apnd file handle.
*/
static int apndOpen(
sqlite3_vfs *pVfs,
sqlite3_vfs *pApndVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
ApndFile *p;
sqlite3_file *pSubFile;
sqlite3_vfs *pSubVfs;
ApndFile *pApndFile = (ApndFile*)pFile;
sqlite3_file *pBaseFile = ORIGFILE(pFile);
sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
int rc;
sqlite3_int64 sz;
pSubVfs = ORIGVFS(pVfs);
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
/* The appendvfs is not to be used for transient or temporary databases. */
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
/* The appendvfs is not to be used for transient or temporary databases.
* Just use the base VFS open to initialize the given file object and
* open the underlying file. (Appendvfs is then unused for this file.)
*/
return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
}
p = (ApndFile*)pFile;
memset(p, 0, sizeof(*p));
pSubFile = ORIGFILE(pFile);
memset(pApndFile, 0, sizeof(ApndFile));
pFile->pMethods = &apnd_io_methods;
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
/* Record that append mark has not been written until seen otherwise. */
pApndFile->iMark = -1;
rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
if( rc ) goto apnd_open_done;
rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
if( rc ){
pSubFile->pMethods->xClose(pSubFile);
pBaseFile->pMethods->xClose(pBaseFile);
goto apnd_open_done;
}
if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
memmove(pFile, pSubFile, pSubVfs->szOsFile);
if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
/* The file being opened appears to be just an ordinary DB. Copy
* the base dispatch-table so this instance mimics the base VFS.
*/
memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
return SQLITE_OK;
}
/* Record that append mark has not been written until seen otherwise. */
p->iMark = -1;
p->iPgOne = apndReadMark(sz, pFile);
if( p->iPgOne>=0 ){
pApndFile->iPgOne = apndReadMark(sz, pFile);
if( pApndFile->iPgOne>=0 ){
/* Append mark was found, infer its offset */
p->iMark = sz - p->iPgOne - APND_MARK_SIZE;
pApndFile->iMark = sz - APND_MARK_SIZE;
/* pApndFile->base = pBaseFile; */
return SQLITE_OK;
}
if( (flags & SQLITE_OPEN_CREATE)==0 ){
pSubFile->pMethods->xClose(pSubFile);
pBaseFile->pMethods->xClose(pBaseFile);
rc = SQLITE_CANTOPEN;
}
/* Round newly added appendvfs location to #define'd page boundary.
* Note that nothing has yet been written to the underlying file.
* The append mark will be written along with first content write.
* Until then, the p->iMark value indicates it is not yet written.
* Until then, paf->iMark value indicates it is not yet written.
*/
p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
pApndFile->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
apnd_open_done:
if( rc ) pFile->pMethods = 0;
return rc;