From: dan Date: Wed, 30 Mar 2011 19:08:03 +0000 (+0000) Subject: Further tests for os_unix.c. X-Git-Tag: version-3.7.6~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=661d71af8c1a47ebef22904e44cb9bce3b81bc3e;p=thirdparty%2Fsqlite.git Further tests for os_unix.c. FossilOrigin-Name: a84f7711949ea3885b0e36e48118d2c76a8a5b82 --- diff --git a/manifest b/manifest index 71c1fddc9a..cb8d6273aa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sgenerate\ssqlite_stat1\sentries\sfor\sempty\stables\swhen\srunning\nANALYZE.\s\sTicket\s[83ea97620bd31016451] -D 2011-03-30T14:54:05.390 +C Further\stests\sfor\sos_unix.c. +D 2011-03-30T19:08:03.321 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -162,7 +162,7 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 2596fd2d5d0976c6c0c628d0c3c7c4e7a724f4cf -F src/os_unix.c 0b37759312e8adb58c0c7dab1ab8ca16957bf299 +F src/os_unix.c be9f9d3383a7b556a1f3805442e0061e436eb7ac F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845 F src/pager.c 6aa906b60a59664ba58d3f746164bb010d407ce1 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 @@ -185,7 +185,7 @@ F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c b020ebf3b4af58cae7875e217efd7ac22f485713 -F src/test1.c 9020310c7617234b33fd1c3064f89524db25f290 +F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee @@ -220,7 +220,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6 F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd -F src/test_syscall.c d12e8cd163cd33b66d0a3d1b8daaf136d09d65c2 +F src/test_syscall.c 349a2b913e82b029f01527f58f65d66a02a09a84 F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0 F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86 @@ -672,8 +672,8 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082 F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 -F test/syscall.test 125d9781d914c408e8629053b5f914dc920ab3eb -F test/sysfault.test be42aa42f89a82305cf3807047d419595e430480 +F test/syscall.test d1dae1fee88613cf763d97ad0038d867509e0c42 +F test/sysfault.test a2c3ca66d82f3b4ac7d29f1aeaba8962f4f5a22a F test/table.test 04ba066432430657712d167ebf28080fe878d305 F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3 @@ -828,6 +828,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150 F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2 +F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09 @@ -919,7 +920,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 3d2de011814002e2e25b7645f94ff8fc7aab9cdd -R 0216cf925df143bd6cbc69301b702732 -U drh -Z ebdb63d4898403f81a888e3e3a66ac94 +P 3a27af5b3c688c651ba1fae261026ef77b7ff5e3 +R 8aec69201b7794049bf0ef28fa4da45f +U dan +Z 9b217166a2b3bec7df70dcbc250ff848 diff --git a/manifest.uuid b/manifest.uuid index 4c1fb6bb6a..d8ae442157 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3a27af5b3c688c651ba1fae261026ef77b7ff5e3 \ No newline at end of file +a84f7711949ea3885b0e36e48118d2c76a8a5b82 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index d958b6fc93..bf11e678af 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -593,9 +593,22 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){ */ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { switch (posixError) { +#if 0 + /* At one point this code was not commented out. In theory, this branch + ** should never be hit, as this function should only be called after + ** a locking-related function (i.e. fcntl()) has returned non-zero with + ** the value of errno as the first argument. Since a system call has failed, + ** errno should be non-zero. + ** + ** Despite this, if errno really is zero, we still don't want to return + ** SQLITE_OK. The system call failed, and *some* SQLite error should be + ** propagated back to the caller. Commenting this branch out means errno==0 + ** will be handled by the "default:" case below. + */ case 0: return SQLITE_OK; - +#endif + case EAGAIN: case ETIMEDOUT: case EBUSY: @@ -1037,7 +1050,7 @@ static void closePendingFds(unixFile *pFile){ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); - if( pInode ){ + if( ALWAYS(pInode) ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); @@ -1211,6 +1224,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ ** This function is a pass-through to fcntl(F_SETLK) if pFile is using ** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" ** and is read-only. +** +** Zero is returned if the call completes successfully, or -1 if a call +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). */ static int unixFileLock(unixFile *pFile, struct flock *pLock){ int rc; @@ -1307,7 +1323,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; - int s = 0; int tErrno = 0; assert( pFile ); @@ -1376,11 +1391,10 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; - s = unixFileLock(pFile, &lock); - if( s==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -1394,33 +1408,31 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( eFileLock==SHARED_LOCK ){ assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); + assert( rc==SQLITE_OK ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( (s = unixFileLock(pFile, &lock))==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } + /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; - if( unixFileLock(pFile, &lock)!=0 ){ - if( s != -1 ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - } + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ + /* This could happen with a network mount */ + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); } - if( s==(-1) ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + + if( rc ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } + goto end_lock; }else{ pFile->eFileLock = SHARED_LOCK; pInode->nLock++; @@ -1437,22 +1449,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ */ assert( 0!=pFile->eFileLock ); lock.l_type = F_WRLCK; - switch( eFileLock ){ - case RESERVED_LOCK: - lock.l_start = RESERVED_BYTE; - break; - case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - break; - default: - assert(0); + + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); + if( eFileLock==RESERVED_LOCK ){ + lock.l_start = RESERVED_BYTE; + lock.l_len = 1L; + }else{ + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; } - s = unixFileLock(pFile, &lock); - if( s==(-1) ){ + + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } } @@ -1623,10 +1633,10 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( unixFileLock(pFile, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_unlock; @@ -1637,12 +1647,12 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( unixFileLock(pFile, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = SHARED_LOCK; }else{ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_unlock; @@ -1661,12 +1671,12 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); - if( unixFileLock(pFile, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } pInode->eFileLock = NO_LOCK; @@ -1714,29 +1724,27 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; - if( pFile ){ - if( pFile->dirfd>=0 ){ - robust_close(pFile, pFile->dirfd, __LINE__); - pFile->dirfd=-1; - } - if( pFile->h>=0 ){ - robust_close(pFile, pFile->h, __LINE__); - pFile->h = -1; - } + if( pFile->dirfd>=0 ){ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd=-1; + } + if( pFile->h>=0 ){ + robust_close(pFile, pFile->h, __LINE__); + pFile->h = -1; + } #if OS_VXWORKS - if( pFile->pId ){ - if( pFile->isDelete ){ - unlink(pFile->pId->zCanonicalName); - } - vxworksReleaseFileId(pFile->pId); - pFile->pId = 0; + if( pFile->pId ){ + if( pFile->isDelete ){ + unlink(pFile->pId->zCanonicalName); } -#endif - OSTRACE(("CLOSE %-3d\n", pFile->h)); - OpenCounter(-1); - sqlite3_free(pFile->pUnused); - memset(pFile, 0, sizeof(unixFile)); + vxworksReleaseFileId(pFile->pId); + pFile->pId = 0; } +#endif + OSTRACE(("CLOSE %-3d\n", pFile->h)); + OpenCounter(-1); + sqlite3_free(pFile->pUnused); + memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } @@ -1745,24 +1753,25 @@ static int closeUnixFile(sqlite3_file *id){ */ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile *)id; - unixUnlock(id, NO_LOCK); - unixEnterMutex(); - assert( pFile->pInode==0 || pFile->pInode->nLock>0 - || pFile->pInode->bProcessLock==0 ); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - rc = closeUnixFile(id); - unixLeaveMutex(); + unixFile *pFile = (unixFile *)id; + unixUnlock(id, NO_LOCK); + unixEnterMutex(); + + /* unixFile.pInode is always valid here. Otherwise, a different close + ** routine (e.g. nolockClose()) would be called instead. + */ + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); + if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->pUnused list. It will be automatically closed + ** when the last lock is cleared. + */ + setPendingFd(pFile); } + releaseInodeInfo(pFile); + rc = closeUnixFile(id); + unixLeaveMutex(); return rc; } @@ -3007,6 +3016,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); #else newOffset = lseek(id->h, offset, SEEK_SET); + SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; @@ -3375,12 +3385,16 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ + #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - int rc; + /* The code below is handling the return value of osFallocate() + ** correctly. posix_fallocate() is defined to "returns zero on success, + ** or an error number on failure". See the manpage for details. */ + int err; do{ - rc = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); - }while( rc<0 && errno==EINTR ); - if( rc ) return SQLITE_IOERR_WRITE; + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); + }while( err==EINTR ); + if( err ) return SQLITE_IOERR_WRITE; #else /* If the OS does not have posix_fallocate(), fake it. First use ** ftruncate() to set the file size, then write a single byte to diff --git a/src/test1.c b/src/test1.c index 852ecdecc7..8a0d09a716 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4888,6 +4888,44 @@ static int file_control_chunksize_test( return TCL_OK; } +/* +** tclcmd: file_control_sizehint_test DB DBNAME SIZE +** +** This TCL command runs the sqlite3_file_control interface and +** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and +** SQLITE_SET_LOCKPROXYFILE verbs. +*/ +static int file_control_sizehint_test( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3_int64 nSize; /* Hinted size */ + char *zDb; /* Db name ("main", "temp" etc.) */ + sqlite3 *db; /* Database handle */ + int rc; /* file_control() return code */ + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetWideIntFromObj(interp, objv[3], &nSize) + ){ + return TCL_ERROR; + } + zDb = Tcl_GetString(objv[2]); + if( zDb[0]=='\0' ) zDb = NULL; + + rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize); + if( rc ){ + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + return TCL_ERROR; + } + return TCL_OK; +} + /* ** tclcmd: file_control_lockproxy_test DB PWD ** @@ -5608,6 +5646,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "file_control_chunksize_test", file_control_chunksize_test, 0 }, + { "file_control_sizehint_test", file_control_sizehint_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, { "sqlite3_create_function_v2", test_create_function_v2, 0 }, diff --git a/src/test_syscall.c b/src/test_syscall.c index 61508ec450..dbac01d69b 100644 --- a/src/test_syscall.c +++ b/src/test_syscall.c @@ -121,7 +121,7 @@ struct TestSyscallArray { /* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 }, /* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 }, /* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 }, - /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, 0, 0 }, + /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, EACCES, 0 }, /* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 }, /* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 }, /* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 }, @@ -275,7 +275,7 @@ static int ts_ftruncate(int fd, off_t n){ static int ts_fcntl(int fd, int cmd, ... ){ va_list ap; void *pArg; - if( tsIsFail() ){ + if( tsIsFailErrno("fcntl") ){ return -1; } va_start(ap, cmd); @@ -287,7 +287,7 @@ static int ts_fcntl(int fd, int cmd, ... ){ ** A wrapper around read(). */ static int ts_read(int fd, void *aBuf, size_t nBuf){ - if( tsIsFail() ){ + if( tsIsFailErrno("read") ){ return -1; } return orig_read(fd, aBuf, nBuf); @@ -297,7 +297,7 @@ static int ts_read(int fd, void *aBuf, size_t nBuf){ ** A wrapper around pread(). */ static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){ - if( tsIsFail() ){ + if( tsIsFailErrno("pread") ){ return -1; } return orig_pread(fd, aBuf, nBuf, off); @@ -307,7 +307,7 @@ static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){ ** A wrapper around pread64(). */ static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){ - if( tsIsFail() ){ + if( tsIsFailErrno("pread64") ){ return -1; } return orig_pread64(fd, aBuf, nBuf, off); @@ -317,7 +317,7 @@ static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){ ** A wrapper around write(). */ static int ts_write(int fd, const void *aBuf, size_t nBuf){ - if( tsIsFail() ){ + if( tsIsFailErrno("write") ){ return -1; } return orig_write(fd, aBuf, nBuf); @@ -327,7 +327,7 @@ static int ts_write(int fd, const void *aBuf, size_t nBuf){ ** A wrapper around pwrite(). */ static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){ - if( tsIsFail() ){ + if( tsIsFailErrno("pwrite") ){ return -1; } return orig_pwrite(fd, aBuf, nBuf, off); @@ -337,7 +337,7 @@ static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){ ** A wrapper around pwrite64(). */ static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){ - if( tsIsFail() ){ + if( tsIsFailErrno("pwrite64") ){ return -1; } return orig_pwrite64(fd, aBuf, nBuf, off); @@ -531,11 +531,17 @@ static int test_syscall_errno( const char *z; int i; } aErrno[] = { - { "EACCES", EACCES }, - { "EINTR", EINTR }, - { "EIO", EIO }, + { "EACCES", EACCES }, + { "EINTR", EINTR }, + { "EIO", EIO }, { "EOVERFLOW", EOVERFLOW }, - { "ENOMEM", ENOMEM }, + { "ENOMEM", ENOMEM }, + { "EAGAIN", EAGAIN }, + { "ETIMEDOUT", ETIMEDOUT }, + { "EBUSY", EBUSY }, + { "EPERM", EPERM }, + { "EDEADLK", EDEADLK }, + { "ENOLCK", ENOLCK }, { 0, 0 } }; diff --git a/test/syscall.test b/test/syscall.test index 610c529866..21295a59cf 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -194,6 +194,57 @@ do_test 6.2 { db close } {} +#------------------------------------------------------------------------- +# Test that a database file a single byte in size is treated as an empty +# file. Whereas a file 2 bytes or larger might be considered corrupt. +# +catch { db close } +forcedelete test.db test.db2 + +proc create_db_file {nByte} { + set fd [open test.db w] + fconfigure $fd -translation binary -encoding binary + puts -nonewline $fd [string range "xSQLite" 1 $nByte] + close $fd +} + +foreach {nByte res} { + 1 {0 {}} + 2 {1 {file is encrypted or is not a database}} + 3 {1 {file is encrypted or is not a database}} +} { + do_test 7.$nByte { + create_db_file $nByte + sqlite3 db test.db + catchsql { CREATE TABLE t1(a, b) } + } $res + catch { db close } +} + +#------------------------------------------------------------------------- +# +catch { db close } +forcedelete test.db test.db2 + +do_test 8.1 { + sqlite3 db test.db + file_control_chunksize_test db main 4096 + file size test.db +} {0} + +foreach {tn hint size} { + 1 1000 4096 + 2 1000 4096 + 3 3000 4096 + 4 4096 4096 + 5 4197 8192 +} { + do_test 8.2.$tn { + file_control_sizehint_test db main $hint + file size test.db + } $size +} + finish_test diff --git a/test/sysfault.test b/test/sysfault.test index f6d1eabbc1..c8c6f512f1 100644 --- a/test/sysfault.test +++ b/test/sysfault.test @@ -66,7 +66,11 @@ do_faultsim_test 1 -faults vfsfault-* -prep { {1 {attempt to write a readonly database}} } -# Errors in the fstat() function when opening and writing a file. +#------------------------------------------------------------------------- +# Errors in the fstat() function when opening and writing a file. Cases +# where fstat() fails and sets errno to ENOMEM and EOVERFLOW are both +# tested. EOVERFLOW is interpreted as meaning that a file on disk is +# too large to be opened by the OS. # foreach {tn errno errlist} { 1 ENOMEM {{disk I/O error}} @@ -85,12 +89,57 @@ foreach {tn errno errlist} { " } +#------------------------------------------------------------------------- +# Various errors in locking functions. +# +foreach vfs {unix unix-excl} { + foreach {tn errno errlist} { + 1 EAGAIN {{database is locked}} + 2 ETIMEDOUT {{database is locked}} + 3 EBUSY {{database is locked}} + 4 EINTR {{database is locked}} + 5 ENOLCK {{database is locked}} + 6 EACCES {{database is locked}} + 7 EPERM {{access permission denied}} + 8 EDEADLK {{disk I/O error}} + 9 ENOMEM {{disk I/O error}} + } { + proc vfsfault_install {} { test_syscall install fcntl } + set errs [list] + foreach e $errlist { lappend errs [list 1 $e] } + + set body [string map [list %VFS% $vfs] { + sqlite3 db test.db + db eval { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + set fd [open test.db-journal w] + puts $fd "hello world" + close $fd + sqlite3 db test.db -vfs %VFS% + db eval { + SELECT * FROM t1; + } + }] + + do_faultsim_test 1.3.$vfs.$tn -faults vfsfault-* -prep { + faultsim_restore + } -body " + test_syscall errno fcntl $errno + $body + " -test " + faultsim_test_result {0 {1 2}} $errs + " + } +} + #------------------------------------------------------------------------- # Check that a single EINTR error does not affect processing. # proc vfsfault_install {} { test_syscall reset - test_syscall install {open ftruncate close} + test_syscall install {open ftruncate close read pread pread64 write fallocate} } forcedelete test.db test.db2 @@ -113,16 +162,25 @@ do_faultsim_test 2.1 -faults vfsfault-transient -prep { test_syscall errno open EINTR test_syscall errno ftruncate EINTR test_syscall errno close EINTR + test_syscall errno read EINTR + test_syscall errno pread EINTR + test_syscall errno pread64 EINTR + test_syscall errno write EINTR + test_syscall errno fallocate EINTR sqlite3 db test.db + file_control_chunksize_test db main 8192 + set res [db eval { ATTACH 'test.db2' AS 'aux'; SELECT * FROM t1; PRAGMA journal_mode = truncate; BEGIN; INSERT INTO t1 VALUES('jkl', 'mno', 'pqr'); + INSERT INTO t1 VALUES(randomblob(10000), 0, 0); UPDATE t2 SET x = 2; COMMIT; + DELETE FROM t1 WHERE length(a)>3; SELECT * FROM t1; SELECT * FROM t2; }] @@ -159,8 +217,31 @@ do_faultsim_test 2.2 -faults vfsfault-* -prep { } #------------------------------------------------------------------------- -# +proc vfsfault_install {} { + test_syscall reset + test_syscall install {fstat fallocate} +} +do_faultsim_test 3 -faults vfsfault-* -prep { + faultsim_delete_and_reopen + file_control_chunksize_test db main 8192 + execsql { + CREATE TABLE t1(a, b); + BEGIN; + SELECT * FROM t1; + } +} -body { + test_syscall errno fstat EIO + test_syscall errno fallocate EIO + + execsql { + INSERT INTO t1 VALUES(randomblob(10000), randomblob(10000)); + SELECT length(a) + length(b) FROM t1; + COMMIT; + } +} -test { + faultsim_test_result {0 20000} +} finish_test diff --git a/test/unixexcl.test b/test/unixexcl.test new file mode 100644 index 0000000000..057ae0af14 --- /dev/null +++ b/test/unixexcl.test @@ -0,0 +1,83 @@ +# 2011 March 30 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains tests for the "unix-excl" VFS module (part of +# os_unix.c). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl + +if {$::tcl_platform(platform)!="unix" || [info commands test_syscall]==""} { + finish_test + return +} +set testprefix unixexcl + + + +# Test that when using VFS "unix-excl", the first time the database is read +# a process-wide exclusive lock is taken on it. This means other connections +# within the process may still access the db normally, but connections from +# outside the process cannot. +# +do_multiclient_test tn { + do_test unixexcl-1.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES('hello', 'world'); + } + } {} + do_test unixexcl-1.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} + do_test unixexcl-1.$tn.3 { + code1 { + db close + sqlite3 db test.db -vfs unix-excl + db eval { SELECT * FROM t1 } + } + } {hello world} + if {$tn==1} { + do_test unixexcl-1.$tn.4.multiproc { + csql2 { SELECT * FROM t1 } + } {1 {database is locked}} + } else { + do_test unixexcl-1.$tn.4.singleproc { + csql2 { SELECT * FROM t1 } + } {0 {hello world}} + } +} + +# Test that when using VFS "unix-excl", if a file is opened in read-only mode +# the behaviour is the same as if VFS "unix" were used. +# +do_multiclient_test tn { + do_test unixexcl-2.$tn.1 { + sql1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES('hello', 'world'); + } + } {} + do_test unixexcl-2.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} + do_test unixexcl-2.$tn.3 { + code1 { + db close + sqlite3 db test.db -readonly yes -vfs unix-excl + db eval { SELECT * FROM t1 } + } + } {hello world} + do_test unixexcl-2.$tn.4 { + csql2 { SELECT * FROM t1 } + } {0 {hello world}} +} + +finish_test