 Chromium Code Reviews
 Chromium Code Reviews Issue 901033002:
  Import SQLite 3.8.7.4.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 901033002:
  Import SQLite 3.8.7.4.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: third_party/sqlite/src/src/os_unix.c | 
| diff --git a/third_party/sqlite/src/src/os_unix.c b/third_party/sqlite/src/src/os_unix.c | 
| index 77ffd8ac0d9a50c0328d03aff3dca52ccd5bb52f..1624f6a2ce220925271aab83826f0ed73219fa39 100644 | 
| --- a/third_party/sqlite/src/src/os_unix.c | 
| +++ b/third_party/sqlite/src/src/os_unix.c | 
| @@ -84,32 +84,6 @@ | 
| #endif | 
| /* | 
| -** These #defines should enable >2GB file support on Posix if the | 
| -** underlying operating system supports it. If the OS lacks | 
| -** large file support, these should be no-ops. | 
| -** | 
| -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch | 
| -** on the compiler command line. This is necessary if you are compiling | 
| -** on a recent machine (ex: RedHat 7.2) but you want your code to work | 
| -** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 | 
| -** without this option, LFS is enable. But LFS does not exist in the kernel | 
| -** in RedHat 6.0, so the code won't work. Hence, for maximum binary | 
| -** portability you should omit LFS. | 
| -** | 
| -** The previous paragraph was written in 2005. (This paragraph is written | 
| -** on 2008-11-28.) These days, all Linux kernels support large files, so | 
| -** you should probably leave LFS enabled. But some embedded platforms might | 
| -** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful. | 
| -*/ | 
| -#ifndef SQLITE_DISABLE_LFS | 
| -# define _LARGE_FILE 1 | 
| -# ifndef _FILE_OFFSET_BITS | 
| -# define _FILE_OFFSET_BITS 64 | 
| -# endif | 
| -# define _LARGEFILE_SOURCE 1 | 
| -#endif | 
| - | 
| -/* | 
| ** standard include files. | 
| */ | 
| #include <sys/types.h> | 
| @@ -119,11 +93,11 @@ | 
| #include <time.h> | 
| #include <sys/time.h> | 
| #include <errno.h> | 
| -#ifndef SQLITE_OMIT_WAL | 
| -#include <sys/mman.h> | 
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 | 
| +# include <sys/mman.h> | 
| #endif | 
| -#if SQLITE_ENABLE_LOCKING_STYLE | 
| +#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS | 
| # include <sys/ioctl.h> | 
| # if OS_VXWORKS | 
| # include <semaphore.h> | 
| @@ -138,6 +112,10 @@ | 
| # include <sys/mount.h> | 
| #endif | 
| +#ifdef HAVE_UTIME | 
| +# include <utime.h> | 
| +#endif | 
| + | 
| /* | 
| ** Allowed values of unixFile.fsFlags | 
| */ | 
| @@ -160,8 +138,8 @@ | 
| #endif | 
| /* | 
| - ** Default permissions when creating auto proxy dir | 
| - */ | 
| +** Default permissions when creating auto proxy dir | 
| +*/ | 
| #ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS | 
| # define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 | 
| #endif | 
| @@ -202,16 +180,28 @@ struct UnixUnusedFd { | 
| typedef struct unixFile unixFile; | 
| struct unixFile { | 
| sqlite3_io_methods const *pMethod; /* Always the first entry */ | 
| + sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ | 
| unixInodeInfo *pInode; /* Info about locks on this inode */ | 
| int h; /* The file descriptor */ | 
| unsigned char eFileLock; /* The type of lock held on this fd */ | 
| - unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ | 
| + unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ | 
| int lastErrno; /* The unix errno from last I/O error */ | 
| void *lockingContext; /* Locking style specific state */ | 
| UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ | 
| const char *zPath; /* Name of the file */ | 
| unixShm *pShm; /* Shared memory segment information */ | 
| int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + int nFetchOut; /* Number of outstanding xFetch refs */ | 
| + sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ | 
| + sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ | 
| + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ | 
| + void *pMapRegion; /* Memory mapped region */ | 
| +#endif | 
| +#ifdef __QNXNTO__ | 
| + int sectorSize; /* Device sector size */ | 
| + int deviceCharacteristics; /* Precomputed device characteristics */ | 
| +#endif | 
| #if SQLITE_ENABLE_LOCKING_STYLE | 
| int openFlags; /* The flags specified at open() */ | 
| #endif | 
| @@ -219,10 +209,9 @@ struct unixFile { | 
| unsigned fsFlags; /* cached details from statfs() */ | 
| #endif | 
| #if OS_VXWORKS | 
| - int isDelete; /* Delete on close if true */ | 
| struct vxworksFileId *pId; /* Unique file ID */ | 
| #endif | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* The next group of variables are used to track whether or not the | 
| ** transaction counter in bytes 24-27 of database files are updated | 
| ** whenever any part of the database changes. An assertion fault will | 
| @@ -233,7 +222,9 @@ struct unixFile { | 
| unsigned char transCntrChng; /* True if the transaction counter changed */ | 
| unsigned char dbUpdate; /* True if any part of database file changed */ | 
| unsigned char inNormalWrite; /* True if in a normal write operation */ | 
| + | 
| #endif | 
| + | 
| #ifdef SQLITE_TEST | 
| /* In test mode, increase the size of this structure a bit so that | 
| ** it is larger than the struct CrashFile defined in test6.c. | 
| @@ -242,12 +233,28 @@ struct unixFile { | 
| #endif | 
| }; | 
| +/* This variable holds the process id (pid) from when the xRandomness() | 
| +** method was called. If xOpen() is called from a different process id, | 
| +** indicating that a fork() has occurred, the PRNG will be reset. | 
| +*/ | 
| +static int randomnessPid = 0; | 
| + | 
| /* | 
| ** Allowed values for the unixFile.ctrlFlags bitmask: | 
| */ | 
| -#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ | 
| -#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ | 
| -#define UNIXFILE_DIRSYNC 0x04 /* Directory sync needed */ | 
| +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ | 
| +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ | 
| +#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ | 
| +#ifndef SQLITE_DISABLE_DIRSYNC | 
| +# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ | 
| +#else | 
| +# define UNIXFILE_DIRSYNC 0x00 | 
| +#endif | 
| +#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ | 
| +#define UNIXFILE_DELETE 0x20 /* Delete on close */ | 
| +#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ | 
| +#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ | 
| +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ | 
| /* | 
| ** Include code that is common to all os_*.c files | 
| @@ -281,8 +288,53 @@ struct unixFile { | 
| #define threadid 0 | 
| #endif | 
| +/* | 
| +** HAVE_MREMAP defaults to true on Linux and false everywhere else. | 
| +*/ | 
| +#if !defined(HAVE_MREMAP) | 
| +# if defined(__linux__) && defined(_GNU_SOURCE) | 
| +# define HAVE_MREMAP 1 | 
| +# else | 
| +# define HAVE_MREMAP 0 | 
| +# endif | 
| +#endif | 
| + | 
| +/* | 
| +** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() | 
| +** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. | 
| +*/ | 
| +#ifdef __ANDROID__ | 
| +# define lseek lseek64 | 
| +#endif | 
| + | 
| +/* | 
| +** Different Unix systems declare open() in different ways. Same use | 
| +** open(const char*,int,mode_t). Others use open(const char*,int,...). | 
| +** The difference is important when using a pointer to the function. | 
| +** | 
| +** The safest way to deal with the problem is to always use this wrapper | 
| +** which always has the same well-defined interface. | 
| +*/ | 
| +static int posixOpen(const char *zFile, int flags, int mode){ | 
| + return open(zFile, flags, mode); | 
| +} | 
| + | 
| +/* | 
| +** On some systems, calls to fchown() will trigger a message in a security | 
| +** log if they come from non-root processes. So avoid calling fchown() if | 
| +** we are not running as root. | 
| +*/ | 
| +static int posixFchown(int fd, uid_t uid, gid_t gid){ | 
| +#if OS_VXWORKS | 
| + return 0; | 
| +#else | 
| + return geteuid() ? 0 : fchown(fd,uid,gid); | 
| +#endif | 
| +} | 
| + | 
| /* Forward reference */ | 
| static int openDirectory(const char*, int*); | 
| +static int unixGetpagesize(void); | 
| /* | 
| ** Many system calls are accessed through pointer-to-functions so that | 
| @@ -291,12 +343,12 @@ static int openDirectory(const char*, int*); | 
| ** to all overrideable system calls. | 
| */ | 
| static struct unix_syscall { | 
| - const char *zName; /* Name of the sytem call */ | 
| + const char *zName; /* Name of the system call */ | 
| sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ | 
| sqlite3_syscall_ptr pDefault; /* Default value */ | 
| } aSyscall[] = { | 
| - { "open", (sqlite3_syscall_ptr)open, 0 }, | 
| -#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent) | 
| + { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, | 
| +#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) | 
| { "close", (sqlite3_syscall_ptr)close, 0 }, | 
| #define osClose ((int(*)(int))aSyscall[1].pCurrent) | 
| @@ -333,7 +385,7 @@ static struct unix_syscall { | 
| { "read", (sqlite3_syscall_ptr)read, 0 }, | 
| #define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) | 
| -#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) | 
| +#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) | 
| { "pread", (sqlite3_syscall_ptr)pread, 0 }, | 
| #else | 
| { "pread", (sqlite3_syscall_ptr)0, 0 }, | 
| @@ -350,7 +402,7 @@ static struct unix_syscall { | 
| { "write", (sqlite3_syscall_ptr)write, 0 }, | 
| #define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) | 
| -#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) | 
| +#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) | 
| { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, | 
| #else | 
| { "pwrite", (sqlite3_syscall_ptr)0, 0 }, | 
| @@ -366,11 +418,7 @@ static struct unix_syscall { | 
| #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ | 
| aSyscall[13].pCurrent) | 
| -#if SQLITE_ENABLE_LOCKING_STYLE | 
| { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, | 
| -#else | 
| - { "fchmod", (sqlite3_syscall_ptr)0, 0 }, | 
| -#endif | 
| #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) | 
| #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE | 
| @@ -386,6 +434,33 @@ static struct unix_syscall { | 
| { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 }, | 
| #define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) | 
| + { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 }, | 
| +#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) | 
| + | 
| + { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, | 
| +#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) | 
| + | 
| + { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, | 
| +#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) | 
| + | 
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 | 
| + { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, | 
| +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) | 
| + | 
| + { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, | 
| +#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent) | 
| + | 
| +#if HAVE_MREMAP | 
| + { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, | 
| +#else | 
| + { "mremap", (sqlite3_syscall_ptr)0, 0 }, | 
| +#endif | 
| +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent) | 
| + { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, | 
| +#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent) | 
| + | 
| +#endif | 
| + | 
| }; /* End of the overrideable system calls */ | 
| /* | 
| @@ -472,12 +547,66 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ | 
| } | 
| /* | 
| -** Retry open() calls that fail due to EINTR | 
| +** Do not accept any file descriptor less than this value, in order to avoid | 
| +** opening database file using file descriptors that are commonly used for | 
| +** standard input, output, and error. | 
| */ | 
| -static int robust_open(const char *z, int f, int m){ | 
| - int rc; | 
| - do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR ); | 
| - return rc; | 
| +#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR | 
| +# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3 | 
| +#endif | 
| + | 
| +/* | 
| +** Invoke open(). Do so multiple times, until it either succeeds or | 
| +** fails for some reason other than EINTR. | 
| +** | 
| +** If the file creation mode "m" is 0 then set it to the default for | 
| +** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally | 
| +** 0644) as modified by the system umask. If m is not 0, then | 
| +** make the file creation mode be exactly m ignoring the umask. | 
| +** | 
| +** The m parameter will be non-zero only when creating -wal, -journal, | 
| +** and -shm files. We want those files to have *exactly* the same | 
| +** permissions as their original database, unadulterated by the umask. | 
| +** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a | 
| +** transaction crashes and leaves behind hot journals, then any | 
| +** process that is able to write to the database will also be able to | 
| +** recover the hot journals. | 
| +*/ | 
| +static int robust_open(const char *z, int f, mode_t m){ | 
| + int fd; | 
| + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; | 
| + while(1){ | 
| +#if defined(O_CLOEXEC) | 
| + fd = osOpen(z,f|O_CLOEXEC,m2); | 
| +#else | 
| + fd = osOpen(z,f,m2); | 
| +#endif | 
| + if( fd<0 ){ | 
| + if( errno==EINTR ) continue; | 
| + break; | 
| + } | 
| + if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; | 
| + osClose(fd); | 
| + sqlite3_log(SQLITE_WARNING, | 
| + "attempt to open \"%s\" as file descriptor %d", z, fd); | 
| + fd = -1; | 
| + if( osOpen("/dev/null", f, m)<0 ) break; | 
| + } | 
| + if( fd>=0 ){ | 
| + if( m!=0 ){ | 
| + struct stat statbuf; | 
| + if( osFstat(fd, &statbuf)==0 | 
| + && statbuf.st_size==0 | 
| + && (statbuf.st_mode&0777)!=m | 
| + ){ | 
| + osFchmod(fd, m); | 
| + } | 
| + } | 
| +#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) | 
| + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | 
| +#endif | 
| + } | 
| + return fd; | 
| } | 
| /* | 
| @@ -507,10 +636,10 @@ static int unixMutexHeld(void) { | 
| #endif | 
| -#ifdef SQLITE_DEBUG | 
| +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) | 
| /* | 
| ** Helper function for printing out trace information from debugging | 
| -** binaries. This returns the string represetation of the supplied | 
| +** binaries. This returns the string representation of the supplied | 
| ** integer lock-type. | 
| */ | 
| static const char *azFileLock(int eFileLock){ | 
| @@ -587,9 +716,22 @@ static int lockTrace(int fd, int op, struct flock *p){ | 
| /* | 
| ** Retry ftruncate() calls that fail due to EINTR | 
| +** | 
| +** All calls to ftruncate() within this file should be made through this wrapper. | 
| +** On the Android platform, bypassing the logic below could lead to a corrupt | 
| +** database. | 
| */ | 
| static int robust_ftruncate(int h, sqlite3_int64 sz){ | 
| int rc; | 
| +#ifdef __ANDROID__ | 
| + /* On Android, ftruncate() always uses 32-bit offsets, even if | 
| + ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to | 
| + ** truncate a file to any size larger than 2GiB. Silently ignore any | 
| + ** such attempts. */ | 
| + if( sz>(sqlite3_int64)0x7FFFFFFF ){ | 
| + rc = SQLITE_OK; | 
| + }else | 
| +#endif | 
| do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); | 
| return rc; | 
| } | 
| @@ -634,25 +776,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { | 
| case EACCES: | 
| /* EACCES is like EAGAIN during locking operations, but not any other time*/ | 
| if( (sqliteIOErr == SQLITE_IOERR_LOCK) || | 
| - (sqliteIOErr == SQLITE_IOERR_UNLOCK) || | 
| - (sqliteIOErr == SQLITE_IOERR_RDLOCK) || | 
| - (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ | 
| + (sqliteIOErr == SQLITE_IOERR_UNLOCK) || | 
| + (sqliteIOErr == SQLITE_IOERR_RDLOCK) || | 
| + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ | 
| return SQLITE_BUSY; | 
| } | 
| /* else fall through */ | 
| case EPERM: | 
| return SQLITE_PERM; | 
| - /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And | 
| - ** this module never makes such a call. And the code in SQLite itself | 
| - ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons | 
| - ** this case is also commented out. If the system does set errno to EDEADLK, | 
| - ** the default SQLITE_IOERR_XXX code will be returned. */ | 
| -#if 0 | 
| - case EDEADLK: | 
| - return SQLITE_IOERR_BLOCKED; | 
| -#endif | 
| - | 
| #if EOPNOTSUPP!=ENOTSUP | 
| case EOPNOTSUPP: | 
| /* something went terribly awry, unless during file system support | 
| @@ -670,7 +802,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { | 
| case ENODEV: | 
| case ENXIO: | 
| case ENOENT: | 
| +#ifdef ESTALE /* ESTALE is not defined on Interix systems */ | 
| case ESTALE: | 
| +#endif | 
| case ENOSYS: | 
| /* these should force the client to close the file and reconnect */ | 
| @@ -680,7 +814,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { | 
| } | 
| - | 
| /****************************************************************************** | 
| ****************** Begin Unique File ID Utility Used By VxWorks *************** | 
| ** | 
| @@ -942,7 +1075,7 @@ struct unixInodeInfo { | 
| UnixUnusedFd *pUnused; /* Unused file descriptors to close */ | 
| unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ | 
| unixInodeInfo *pPrev; /* .... doubly linked */ | 
| -#if defined(SQLITE_ENABLE_LOCKING_STYLE) | 
| +#if SQLITE_ENABLE_LOCKING_STYLE | 
| unsigned long long sharedByte; /* for AFP simulated shared lock */ | 
| #endif | 
| #if OS_VXWORKS | 
| @@ -969,7 +1102,7 @@ static unixInodeInfo *inodeList = 0; | 
| ** The first argument passed to the macro should be the error code that | 
| ** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). | 
| ** The two subsequent arguments should be the name of the OS function that | 
| -** failed (e.g. "unlink", "open") and the the associated file-system path, | 
| +** failed (e.g. "unlink", "open") and the associated file-system path, | 
| ** if any. | 
| */ | 
| #define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) | 
| @@ -992,7 +1125,7 @@ static int unixLogErrorAtLine( | 
| zErr = aErr; | 
| /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, | 
| - ** assume that the system provides the the GNU version of strerror_r() that | 
| + ** assume that the system provides the GNU version of strerror_r() that | 
| ** returns a pointer to a buffer containing the error message. That pointer | 
| ** may point to aErr[], or it may point to some static storage somewhere. | 
| ** Otherwise, assume that the system provides the POSIX version of | 
| @@ -1016,7 +1149,6 @@ static int unixLogErrorAtLine( | 
| zErr = strerror(iErrno); | 
| #endif | 
| - assert( errcode!=SQLITE_OK ); | 
| if( zPath==0 ) zPath = ""; | 
| sqlite3_log(errcode, | 
| "os_unix.c:%d: (%d) %s(%s) - %s", | 
| @@ -1181,6 +1313,66 @@ static int findInodeInfo( | 
| return SQLITE_OK; | 
| } | 
| +/* | 
| +** Return TRUE if pFile has been renamed or unlinked since it was first opened. | 
| +*/ | 
| +static int fileHasMoved(unixFile *pFile){ | 
| +#if OS_VXWORKS | 
| + return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; | 
| +#else | 
| + struct stat buf; | 
| + | 
| + /* TODO(shess): This check doesn't work when the Chromium's WebDB code is | 
| + ** running in the sandbox. | 
| 
michaeln
2015/02/09 20:35:51
Does this have any impact on sqlite consumers in t
 
Scott Hess - ex-Googler
2015/02/09 21:36:27
The intention is to notice cases where the underly
 | 
| + */ | 
| + return 0; | 
| + | 
| + return pFile->pInode!=0 && | 
| + (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino); | 
| +#endif | 
| +} | 
| + | 
| + | 
| +/* | 
| +** Check a unixFile that is a database. Verify the following: | 
| +** | 
| +** (1) There is exactly one hard link on the file | 
| +** (2) The file is not a symbolic link | 
| +** (3) The file has not been renamed or unlinked | 
| +** | 
| +** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. | 
| +*/ | 
| +static void verifyDbFile(unixFile *pFile){ | 
| + struct stat buf; | 
| + int rc; | 
| + if( pFile->ctrlFlags & UNIXFILE_WARNED ){ | 
| + /* One or more of the following warnings have already been issued. Do not | 
| + ** repeat them so as not to clutter the error log */ | 
| + return; | 
| + } | 
| + rc = osFstat(pFile->h, &buf); | 
| + if( rc!=0 ){ | 
| + sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); | 
| + pFile->ctrlFlags |= UNIXFILE_WARNED; | 
| + return; | 
| + } | 
| + if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){ | 
| + sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); | 
| + pFile->ctrlFlags |= UNIXFILE_WARNED; | 
| + return; | 
| + } | 
| + if( buf.st_nlink>1 ){ | 
| + sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); | 
| + pFile->ctrlFlags |= UNIXFILE_WARNED; | 
| + return; | 
| + } | 
| + if( fileHasMoved(pFile) ){ | 
| + sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); | 
| + pFile->ctrlFlags |= UNIXFILE_WARNED; | 
| + return; | 
| + } | 
| +} | 
| + | 
| /* | 
| ** This routine checks if there is a RESERVED lock held on the specified | 
| @@ -1340,14 +1532,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){ | 
| */ | 
| int rc = SQLITE_OK; | 
| unixFile *pFile = (unixFile*)id; | 
| - unixInodeInfo *pInode = pFile->pInode; | 
| + unixInodeInfo *pInode; | 
| struct flock lock; | 
| int tErrno = 0; | 
| assert( pFile ); | 
| OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, | 
| azFileLock(eFileLock), azFileLock(pFile->eFileLock), | 
| - azFileLock(pInode->eFileLock), pInode->nShared , getpid())); | 
| + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid())); | 
| /* If there is already a lock of this type or more restrictive on the | 
| ** unixFile, do nothing. Don't use the end_lock: exit path, as | 
| @@ -1488,7 +1680,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ | 
| } | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* Set up the transaction-counter change checking flags when | 
| ** transitioning from a SHARED to a RESERVED lock. The change | 
| ** from SHARED to RESERVED marks the beginning of a normal | 
| @@ -1551,7 +1743,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ | 
| unixInodeInfo *pInode; | 
| struct flock lock; | 
| int rc = SQLITE_OK; | 
| - int h; | 
| assert( pFile ); | 
| OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, | 
| @@ -1563,16 +1754,12 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ | 
| return SQLITE_OK; | 
| } | 
| unixEnterMutex(); | 
| - h = pFile->h; | 
| pInode = pFile->pInode; | 
| assert( pInode->nShared!=0 ); | 
| if( pFile->eFileLock>SHARED_LOCK ){ | 
| assert( pInode->eFileLock==pFile->eFileLock ); | 
| - SimulateIOErrorBenign(1); | 
| - SimulateIOError( h=(-1) ) | 
| - SimulateIOErrorBenign(0); | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* When reducing a lock such that other processes can start | 
| ** reading the database file again, make sure that the | 
| ** transaction counter was updated if any part of the database | 
| @@ -1581,11 +1768,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ | 
| ** the file has changed and hence might not know to flush their | 
| ** cache. The use of a stale cache can lead to database corruption. | 
| */ | 
| -#if 0 | 
| - assert( pFile->inNormalWrite==0 | 
| - || pFile->dbUpdate==0 | 
| - || pFile->transCntrChng==1 ); | 
| -#endif | 
| pFile->inNormalWrite = 0; | 
| #endif | 
| @@ -1687,14 +1869,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ | 
| lock.l_type = F_UNLCK; | 
| lock.l_whence = SEEK_SET; | 
| lock.l_start = lock.l_len = 0L; | 
| - SimulateIOErrorBenign(1); | 
| - SimulateIOError( h=(-1) ) | 
| - SimulateIOErrorBenign(0); | 
| if( unixFileLock(pFile, &lock)==0 ){ | 
| pInode->eFileLock = NO_LOCK; | 
| }else{ | 
| rc = SQLITE_IOERR_UNLOCK; | 
| - pFile->lastErrno = errno; | 
| + pFile->lastErrno = errno; | 
| pInode->eFileLock = NO_LOCK; | 
| pFile->eFileLock = NO_LOCK; | 
| } | 
| @@ -1710,7 +1889,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ | 
| closePendingFds(pFile); | 
| } | 
| } | 
| - | 
| + | 
| end_unlock: | 
| unixLeaveMutex(); | 
| if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; | 
| @@ -1725,9 +1904,17 @@ end_unlock: | 
| ** the requested locking level, this routine is a no-op. | 
| */ | 
| static int unixUnlock(sqlite3_file *id, int eFileLock){ | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); | 
| +#endif | 
| return posixUnlock(id, eFileLock, 0); | 
| } | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| +static int unixMapfile(unixFile *pFd, i64 nByte); | 
| +static void unixUnmapfile(unixFile *pFd); | 
| +#endif | 
| + | 
| /* | 
| ** This function performs the parts of the "close file" operation | 
| ** common to all locking schemes. It closes the directory and file | 
| @@ -1740,19 +1927,29 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ | 
| */ | 
| static int closeUnixFile(sqlite3_file *id){ | 
| unixFile *pFile = (unixFile*)id; | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + unixUnmapfile(pFile); | 
| +#endif | 
| if( pFile->h>=0 ){ | 
| robust_close(pFile, pFile->h, __LINE__); | 
| pFile->h = -1; | 
| } | 
| #if OS_VXWORKS | 
| if( pFile->pId ){ | 
| - if( pFile->isDelete ){ | 
| + if( pFile->ctrlFlags & UNIXFILE_DELETE ){ | 
| osUnlink(pFile->pId->zCanonicalName); | 
| } | 
| vxworksReleaseFileId(pFile->pId); | 
| pFile->pId = 0; | 
| } | 
| #endif | 
| +#ifdef SQLITE_UNLINK_AFTER_CLOSE | 
| + if( pFile->ctrlFlags & UNIXFILE_DELETE ){ | 
| + osUnlink(pFile->zPath); | 
| + sqlite3_free(*(char**)&pFile->zPath); | 
| + pFile->zPath = 0; | 
| + } | 
| +#endif | 
| OSTRACE(("CLOSE %-3d\n", pFile->h)); | 
| OpenCounter(-1); | 
| sqlite3_free(pFile->pUnused); | 
| @@ -1766,6 +1963,7 @@ static int closeUnixFile(sqlite3_file *id){ | 
| static int unixClose(sqlite3_file *id){ | 
| int rc = SQLITE_OK; | 
| unixFile *pFile = (unixFile *)id; | 
| + verifyDbFile(pFile); | 
| unixUnlock(id, NO_LOCK); | 
| unixEnterMutex(); | 
| @@ -1834,9 +2032,9 @@ static int nolockClose(sqlite3_file *id) { | 
| /****************************************************************************** | 
| ************************* Begin dot-file Locking ****************************** | 
| ** | 
| -** The dotfile locking implementation uses the existance of separate lock | 
| -** files in order to control access to the database. This works on just | 
| -** about every filesystem imaginable. But there are serious downsides: | 
| +** The dotfile locking implementation uses the existence of separate lock | 
| +** files (really a directory) to control access to the database. This works | 
| +** on just about every filesystem imaginable. But there are serious downsides: | 
| ** | 
| ** (1) There is zero concurrency. A single reader blocks all other | 
| ** connections from reading or writing the database. | 
| @@ -1847,15 +2045,15 @@ static int nolockClose(sqlite3_file *id) { | 
| ** Nevertheless, a dotlock is an appropriate locking mode for use if no | 
| ** other locking strategy is available. | 
| ** | 
| -** Dotfile locking works by creating a file in the same directory as the | 
| -** database and with the same name but with a ".lock" extension added. | 
| -** The existance of a lock file implies an EXCLUSIVE lock. All other lock | 
| -** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. | 
| +** Dotfile locking works by creating a subdirectory in the same directory as | 
| +** the database and with the same name but with a ".lock" extension added. | 
| +** The existence of a lock directory implies an EXCLUSIVE lock. All other | 
| +** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. | 
| */ | 
| /* | 
| ** The file suffix added to the data base filename in order to create the | 
| -** lock file. | 
| +** lock directory. | 
| */ | 
| #define DOTLOCK_SUFFIX ".lock" | 
| @@ -1922,7 +2120,6 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { | 
| */ | 
| static int dotlockLock(sqlite3_file *id, int eFileLock) { | 
| unixFile *pFile = (unixFile*)id; | 
| - int fd; | 
| char *zLockFile = (char *)pFile->lockingContext; | 
| int rc = SQLITE_OK; | 
| @@ -1932,17 +2129,19 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { | 
| */ | 
| if( pFile->eFileLock > NO_LOCK ){ | 
| pFile->eFileLock = eFileLock; | 
| -#if !OS_VXWORKS | 
| /* Always update the timestamp on the old file */ | 
| +#ifdef HAVE_UTIME | 
| + utime(zLockFile, NULL); | 
| +#else | 
| utimes(zLockFile, NULL); | 
| #endif | 
| return SQLITE_OK; | 
| } | 
| /* grab an exclusive lock */ | 
| - fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); | 
| - if( fd<0 ){ | 
| - /* failed to open/create the file, someone else may have stolen the lock */ | 
| + rc = osMkdir(zLockFile, 0777); | 
| + if( rc<0 ){ | 
| + /* failed to open/create the lock directory */ | 
| int tErrno = errno; | 
| if( EEXIST == tErrno ){ | 
| rc = SQLITE_BUSY; | 
| @@ -1954,7 +2153,6 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { | 
| } | 
| return rc; | 
| } | 
| - robust_close(pFile, fd, __LINE__); | 
| /* got it, set the type and return ok */ | 
| pFile->eFileLock = eFileLock; | 
| @@ -1973,10 +2171,11 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { | 
| static int dotlockUnlock(sqlite3_file *id, int eFileLock) { | 
| unixFile *pFile = (unixFile*)id; | 
| char *zLockFile = (char *)pFile->lockingContext; | 
| + int rc; | 
| assert( pFile ); | 
| OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, | 
| - pFile->eFileLock, getpid())); | 
| + pFile->eFileLock, getpid())); | 
| assert( eFileLock<=SHARED_LOCK ); | 
| /* no-op if possible */ | 
| @@ -1994,9 +2193,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { | 
| /* To fully unlock the database, delete the lock file */ | 
| assert( eFileLock==NO_LOCK ); | 
| - if( osUnlink(zLockFile) ){ | 
| - int rc = 0; | 
| + rc = osRmdir(zLockFile); | 
| + if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile); | 
| + if( rc<0 ){ | 
| int tErrno = errno; | 
| + rc = 0; | 
| if( ENOENT != tErrno ){ | 
| rc = SQLITE_IOERR_UNLOCK; | 
| } | 
| @@ -2013,13 +2214,13 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { | 
| ** Close a file. Make sure the lock has been released before closing. | 
| */ | 
| static int dotlockClose(sqlite3_file *id) { | 
| - int rc; | 
| + int rc = SQLITE_OK; | 
| if( id ){ | 
| unixFile *pFile = (unixFile*)id; | 
| dotlockUnlock(id, NO_LOCK); | 
| sqlite3_free(pFile->lockingContext); | 
| + rc = closeUnixFile(id); | 
| } | 
| - rc = closeUnixFile(id); | 
| return rc; | 
| } | 
| /****************** End of the dot-file lock implementation ******************* | 
| @@ -2223,10 +2424,12 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { | 
| ** Close a file. | 
| */ | 
| static int flockClose(sqlite3_file *id) { | 
| + int rc = SQLITE_OK; | 
| if( id ){ | 
| flockUnlock(id, NO_LOCK); | 
| + rc = closeUnixFile(id); | 
| } | 
| - return closeUnixFile(id); | 
| + return rc; | 
| } | 
| #endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ | 
| @@ -2269,7 +2472,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { | 
| /* Otherwise see if some other process holds it. */ | 
| if( !reserved ){ | 
| sem_t *pSem = pFile->pInode->pSem; | 
| - struct stat statBuf; | 
| if( sem_trywait(pSem)==-1 ){ | 
| int tErrno = errno; | 
| @@ -2322,7 +2524,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { | 
| */ | 
| static int semLock(sqlite3_file *id, int eFileLock) { | 
| unixFile *pFile = (unixFile*)id; | 
| - int fd; | 
| sem_t *pSem = pFile->pInode->pSem; | 
| int rc = SQLITE_OK; | 
| @@ -2361,7 +2562,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) { | 
| assert( pFile ); | 
| assert( pSem ); | 
| OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, | 
| - pFile->eFileLock, getpid())); | 
| + pFile->eFileLock, getpid())); | 
| assert( eFileLock<=SHARED_LOCK ); | 
| /* no-op if possible */ | 
| @@ -2500,11 +2701,12 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){ | 
| int rc = SQLITE_OK; | 
| int reserved = 0; | 
| unixFile *pFile = (unixFile*)id; | 
| + afpLockingContext *context; | 
| SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); | 
| assert( pFile ); | 
| - afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; | 
| + context = (afpLockingContext *) pFile->lockingContext; | 
| if( context->reserved ){ | 
| *pResOut = 1; | 
| return SQLITE_OK; | 
| @@ -2644,7 +2846,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ | 
| ** operating system calls for the specified lock. | 
| */ | 
| if( eFileLock==SHARED_LOCK ){ | 
| - int lrc1, lrc2, lrc1Errno; | 
| + int lrc1, lrc2, lrc1Errno = 0; | 
| long lk, mask; | 
| assert( pInode->nShared==0 ); | 
| @@ -2775,7 +2977,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { | 
| SimulateIOError( h=(-1) ) | 
| SimulateIOErrorBenign(0); | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* When reducing a lock such that other processes can start | 
| ** reading the database file again, make sure that the | 
| ** transaction counter was updated if any part of the database | 
| @@ -2923,7 +3125,7 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ | 
| ** NB: If you define USE_PREAD or USE_PREAD64, then it might also | 
| ** be necessary to define _XOPEN_SOURCE to be 500. This varies from | 
| ** one system to another. Since SQLite does not define USE_PREAD | 
| -** any any form by default, we will not attempt to define _XOPEN_SOURCE. | 
| +** in any form by default, we will not attempt to define _XOPEN_SOURCE. | 
| ** See tickets #2741 and #2681. | 
| ** | 
| ** To avoid stomping the errno value on a failed read the lastErrno value | 
| @@ -2931,35 +3133,51 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ | 
| */ | 
| static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ | 
| int got; | 
| + int prior = 0; | 
| #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) | 
| i64 newOffset; | 
| #endif | 
| TIMER_START; | 
| + assert( cnt==(cnt&0x1ffff) ); | 
| + assert( id->h>2 ); | 
| + cnt &= 0x1ffff; | 
| + do{ | 
| #if defined(USE_PREAD) | 
| - do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); | 
| - SimulateIOError( got = -1 ); | 
| + got = osPread(id->h, pBuf, cnt, offset); | 
| + SimulateIOError( got = -1 ); | 
| #elif defined(USE_PREAD64) | 
| - do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); | 
| - SimulateIOError( got = -1 ); | 
| + got = osPread64(id->h, pBuf, cnt, offset); | 
| + SimulateIOError( got = -1 ); | 
| #else | 
| - newOffset = lseek(id->h, offset, SEEK_SET); | 
| - SimulateIOError( newOffset-- ); | 
| - if( newOffset!=offset ){ | 
| - if( newOffset == -1 ){ | 
| - ((unixFile*)id)->lastErrno = errno; | 
| - }else{ | 
| - ((unixFile*)id)->lastErrno = 0; | 
| + newOffset = lseek(id->h, offset, SEEK_SET); | 
| + SimulateIOError( newOffset-- ); | 
| + if( newOffset!=offset ){ | 
| + if( newOffset == -1 ){ | 
| + ((unixFile*)id)->lastErrno = errno; | 
| + }else{ | 
| + ((unixFile*)id)->lastErrno = 0; | 
| + } | 
| + return -1; | 
| } | 
| - return -1; | 
| - } | 
| - do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); | 
| + got = osRead(id->h, pBuf, cnt); | 
| #endif | 
| + if( got==cnt ) break; | 
| + if( got<0 ){ | 
| + if( errno==EINTR ){ got = 1; continue; } | 
| + prior = 0; | 
| + ((unixFile*)id)->lastErrno = errno; | 
| + break; | 
| + }else if( got>0 ){ | 
| + cnt -= got; | 
| + offset += got; | 
| + prior += got; | 
| + pBuf = (void*)(got + (char*)pBuf); | 
| + } | 
| + }while( got>0 ); | 
| TIMER_END; | 
| - if( got<0 ){ | 
| - ((unixFile*)id)->lastErrno = errno; | 
| - } | 
| - OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); | 
| - return got; | 
| + OSTRACE(("READ %-3d %5d %7lld %llu\n", | 
| + id->h, got+prior, offset-prior, TIMER_ELAPSED)); | 
| + return got+prior; | 
| } | 
| /* | 
| @@ -2976,6 +3194,8 @@ static int unixRead( | 
| unixFile *pFile = (unixFile *)id; | 
| int got; | 
| assert( id ); | 
| + assert( offset>=0 ); | 
| + assert( amt>0 ); | 
| /* If this is a database file (not a journal, master-journal or temp | 
| ** file), the bytes in the locking range should never be read or written. */ | 
| @@ -2986,6 +3206,23 @@ static int unixRead( | 
| ); | 
| #endif | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + /* Deal with as much of this read request as possible by transfering | 
| + ** data from the memory mapping using memcpy(). */ | 
| + if( offset<pFile->mmapSize ){ | 
| + if( offset+amt <= pFile->mmapSize ){ | 
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); | 
| + return SQLITE_OK; | 
| + }else{ | 
| + int nCopy = pFile->mmapSize - offset; | 
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); | 
| + pBuf = &((u8 *)pBuf)[nCopy]; | 
| + amt -= nCopy; | 
| + offset += nCopy; | 
| + } | 
| + } | 
| +#endif | 
| + | 
| got = seekAndRead(pFile, offset, pBuf, amt); | 
| if( got==amt ){ | 
| return SQLITE_OK; | 
| @@ -3001,42 +3238,60 @@ static int unixRead( | 
| } | 
| /* | 
| -** Seek to the offset in id->offset then read cnt bytes into pBuf. | 
| -** Return the number of bytes actually read. Update the offset. | 
| -** | 
| -** To avoid stomping the errno value on a failed write the lastErrno value | 
| -** is set before returning. | 
| +** Attempt to seek the file-descriptor passed as the first argument to | 
| +** absolute offset iOff, then attempt to write nBuf bytes of data from | 
| +** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise, | 
| +** return the actual number of bytes written (which may be less than | 
| +** nBuf). | 
| */ | 
| -static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ | 
| - int got; | 
| -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) | 
| - i64 newOffset; | 
| -#endif | 
| +static int seekAndWriteFd( | 
| + int fd, /* File descriptor to write to */ | 
| + i64 iOff, /* File offset to begin writing at */ | 
| + const void *pBuf, /* Copy data from this buffer to the file */ | 
| + int nBuf, /* Size of buffer pBuf in bytes */ | 
| + int *piErrno /* OUT: Error number if error occurs */ | 
| +){ | 
| + int rc = 0; /* Value returned by system call */ | 
| + | 
| + assert( nBuf==(nBuf&0x1ffff) ); | 
| + assert( fd>2 ); | 
| + nBuf &= 0x1ffff; | 
| TIMER_START; | 
| + | 
| #if defined(USE_PREAD) | 
| - do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); | 
| + do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); | 
| #elif defined(USE_PREAD64) | 
| - do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); | 
| + do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); | 
| #else | 
| - newOffset = lseek(id->h, offset, SEEK_SET); | 
| - SimulateIOError( newOffset-- ); | 
| - if( newOffset!=offset ){ | 
| - if( newOffset == -1 ){ | 
| - ((unixFile*)id)->lastErrno = errno; | 
| - }else{ | 
| - ((unixFile*)id)->lastErrno = 0; | 
| + do{ | 
| + i64 iSeek = lseek(fd, iOff, SEEK_SET); | 
| + SimulateIOError( iSeek-- ); | 
| + | 
| + if( iSeek!=iOff ){ | 
| + if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0); | 
| + return -1; | 
| } | 
| - return -1; | 
| - } | 
| - do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); | 
| + rc = osWrite(fd, pBuf, nBuf); | 
| + }while( rc<0 && errno==EINTR ); | 
| #endif | 
| + | 
| TIMER_END; | 
| - if( got<0 ){ | 
| - ((unixFile*)id)->lastErrno = errno; | 
| - } | 
| + OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); | 
| - OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); | 
| - return got; | 
| + if( rc<0 && piErrno ) *piErrno = errno; | 
| + return rc; | 
| +} | 
| + | 
| + | 
| +/* | 
| +** Seek to the offset in id->offset then read cnt bytes into pBuf. | 
| +** Return the number of bytes actually read. Update the offset. | 
| +** | 
| +** To avoid stomping the errno value on a failed write the lastErrno value | 
| +** is set before returning. | 
| +*/ | 
| +static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ | 
| + return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno); | 
| } | 
| @@ -3064,7 +3319,7 @@ static int unixWrite( | 
| ); | 
| #endif | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* If we are doing a normal write to a database file (as opposed to | 
| ** doing a hot-journal rollback or a write to some file other than a | 
| ** normal database file) then record the fact that the database | 
| @@ -3086,6 +3341,23 @@ static int unixWrite( | 
| } | 
| #endif | 
| +#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( offset<pFile->mmapSize ){ | 
| + if( offset+amt <= pFile->mmapSize ){ | 
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); | 
| + return SQLITE_OK; | 
| + }else{ | 
| + int nCopy = pFile->mmapSize - offset; | 
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); | 
| + pBuf = &((u8 *)pBuf)[nCopy]; | 
| + amt -= nCopy; | 
| + offset += nCopy; | 
| + } | 
| + } | 
| +#endif | 
| + | 
| while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ | 
| amt -= wrote; | 
| offset += wrote; | 
| @@ -3095,7 +3367,7 @@ static int unixWrite( | 
| SimulateDiskfullError(( wrote=0, amt=1 )); | 
| if( amt>0 ){ | 
| - if( wrote<0 ){ | 
| + if( wrote<0 && pFile->lastErrno!=ENOSPC ){ | 
| /* lastErrno set by seekAndWrite */ | 
| return SQLITE_IOERR_WRITE; | 
| }else{ | 
| @@ -3118,11 +3390,11 @@ int sqlite3_fullsync_count = 0; | 
| /* | 
| ** We do not trust systems to provide a working fdatasync(). Some do. | 
| -** Others do no. To be safe, we will stick with the (slower) fsync(). | 
| -** If you know that your system does support fdatasync() correctly, | 
| +** Others do no. To be safe, we will stick with the (slightly slower) | 
| +** fsync(). If you know that your system does support fdatasync() correctly, | 
| ** then simply compile with -Dfdatasync=fdatasync | 
| */ | 
| -#if !defined(fdatasync) && !defined(__linux__) | 
| +#if !defined(fdatasync) | 
| # define fdatasync fsync | 
| #endif | 
| @@ -3264,9 +3536,6 @@ static int openDirectory(const char *zFilename, int *pFd){ | 
| zDirname[ii] = '\0'; | 
| fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); | 
| if( fd>=0 ){ | 
| -#ifdef FD_CLOEXEC | 
| - osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | 
| -#endif | 
| OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); | 
| } | 
| } | 
| @@ -3316,7 +3585,7 @@ static int unixSync(sqlite3_file *id, int flags){ | 
| } | 
| /* Also fsync the directory containing the file if the DIRSYNC flag | 
| - ** is set. This is a one-time occurrance. Many systems (examples: AIX) | 
| + ** is set. This is a one-time occurrence. Many systems (examples: AIX) | 
| ** are unable to fsync a directory, so ignore errors on the fsync. | 
| */ | 
| if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ | 
| @@ -3349,16 +3618,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ | 
| ** actual file size after the operation may be larger than the requested | 
| ** size). | 
| */ | 
| - if( pFile->szChunk ){ | 
| + if( pFile->szChunk>0 ){ | 
| nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; | 
| } | 
| - rc = robust_ftruncate(pFile->h, (off_t)nByte); | 
| + rc = robust_ftruncate(pFile->h, nByte); | 
| if( rc ){ | 
| pFile->lastErrno = errno; | 
| return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); | 
| }else{ | 
| -#ifndef NDEBUG | 
| +#ifdef SQLITE_DEBUG | 
| /* If we are doing a normal write to a database file (as opposed to | 
| ** doing a hot-journal rollback or a write to some file other than a | 
| ** normal database file) and we truncate the file to zero length, | 
| @@ -3371,6 +3640,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ | 
| } | 
| #endif | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + /* If the file was just truncated to a size smaller than the currently | 
| + ** mapped region, reduce the effective mapping size as well. SQLite will | 
| + ** use read() and write() to access data beyond this point from now on. | 
| + */ | 
| + if( nByte<pFile->mmapSize ){ | 
| + pFile->mmapSize = nByte; | 
| + } | 
| +#endif | 
| + | 
| return SQLITE_OK; | 
| } | 
| } | 
| @@ -3412,14 +3691,12 @@ static int proxyFileControl(sqlite3_file*,int,void*); | 
| /* | 
| ** This function is called to handle the SQLITE_FCNTL_SIZE_HINT | 
| -** file-control operation. | 
| -** | 
| -** If the user has configured a chunk-size for this file, it could be | 
| -** that the file needs to be extended at this point. Otherwise, the | 
| -** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix. | 
| +** file-control operation. Enlarge the database to nBytes in size | 
| +** (rounded up to the next chunk-size). If the database is already | 
| +** nBytes or larger, this routine is a no-op. | 
| */ | 
| static int fcntlSizeHint(unixFile *pFile, i64 nByte){ | 
| - if( pFile->szChunk ){ | 
| + if( pFile->szChunk>0 ){ | 
| i64 nSize; /* Required file size */ | 
| struct stat buf; /* Used to hold return values of fstat() */ | 
| @@ -3461,30 +3738,111 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ | 
| } | 
| } | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ | 
| + int rc; | 
| + if( pFile->szChunk<=0 ){ | 
| + if( robust_ftruncate(pFile->h, nByte) ){ | 
| + pFile->lastErrno = errno; | 
| + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); | 
| + } | 
| + } | 
| + | 
| + rc = unixMapfile(pFile, nByte); | 
| + return rc; | 
| + } | 
| +#endif | 
| + | 
| return SQLITE_OK; | 
| } | 
| /* | 
| +** If *pArg is initially negative then this is a query. Set *pArg to | 
| +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. | 
| +** | 
| +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. | 
| +*/ | 
| +static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ | 
| + if( *pArg<0 ){ | 
| + *pArg = (pFile->ctrlFlags & mask)!=0; | 
| + }else if( (*pArg)==0 ){ | 
| + pFile->ctrlFlags &= ~mask; | 
| + }else{ | 
| + pFile->ctrlFlags |= mask; | 
| + } | 
| +} | 
| + | 
| +/* Forward declaration */ | 
| +static int unixGetTempname(int nBuf, char *zBuf); | 
| + | 
| +/* | 
| ** Information and control of an open file handle. | 
| */ | 
| static int unixFileControl(sqlite3_file *id, int op, void *pArg){ | 
| + unixFile *pFile = (unixFile*)id; | 
| switch( op ){ | 
| case SQLITE_FCNTL_LOCKSTATE: { | 
| - *(int*)pArg = ((unixFile*)id)->eFileLock; | 
| + *(int*)pArg = pFile->eFileLock; | 
| return SQLITE_OK; | 
| } | 
| case SQLITE_LAST_ERRNO: { | 
| - *(int*)pArg = ((unixFile*)id)->lastErrno; | 
| + *(int*)pArg = pFile->lastErrno; | 
| return SQLITE_OK; | 
| } | 
| case SQLITE_FCNTL_CHUNK_SIZE: { | 
| - ((unixFile*)id)->szChunk = *(int *)pArg; | 
| + pFile->szChunk = *(int *)pArg; | 
| return SQLITE_OK; | 
| } | 
| case SQLITE_FCNTL_SIZE_HINT: { | 
| - return fcntlSizeHint((unixFile *)id, *(i64 *)pArg); | 
| + int rc; | 
| + SimulateIOErrorBenign(1); | 
| + rc = fcntlSizeHint(pFile, *(i64 *)pArg); | 
| + SimulateIOErrorBenign(0); | 
| + return rc; | 
| + } | 
| + case SQLITE_FCNTL_PERSIST_WAL: { | 
| + unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); | 
| + return SQLITE_OK; | 
| + } | 
| + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { | 
| + unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); | 
| + return SQLITE_OK; | 
| + } | 
| + case SQLITE_FCNTL_VFSNAME: { | 
| + *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); | 
| + return SQLITE_OK; | 
| } | 
| -#ifndef NDEBUG | 
| + case SQLITE_FCNTL_TEMPFILENAME: { | 
| + char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname ); | 
| + if( zTFile ){ | 
| + unixGetTempname(pFile->pVfs->mxPathname, zTFile); | 
| + *(char**)pArg = zTFile; | 
| + } | 
| + return SQLITE_OK; | 
| + } | 
| + case SQLITE_FCNTL_HAS_MOVED: { | 
| + *(int*)pArg = fileHasMoved(pFile); | 
| 
michaeln
2015/02/09 22:55:34
does this one only result in warnings too?
 
Scott Hess - ex-Googler
2015/02/10 21:14:23
I went digging, and this is the operational one.
 | 
| + return SQLITE_OK; | 
| + } | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + case SQLITE_FCNTL_MMAP_SIZE: { | 
| + i64 newLimit = *(i64*)pArg; | 
| + int rc = SQLITE_OK; | 
| + if( newLimit>sqlite3GlobalConfig.mxMmap ){ | 
| + newLimit = sqlite3GlobalConfig.mxMmap; | 
| + } | 
| + *(i64*)pArg = pFile->mmapSizeMax; | 
| + if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ | 
| + pFile->mmapSizeMax = newLimit; | 
| + if( pFile->mmapSize>0 ){ | 
| + unixUnmapfile(pFile); | 
| + rc = unixMapfile(pFile, -1); | 
| + } | 
| + } | 
| + return rc; | 
| + } | 
| +#endif | 
| +#ifdef SQLITE_DEBUG | 
| /* The pager calls this method to signal that it has done | 
| ** a rollback and that the database is therefore unchanged and | 
| ** it hence it is OK for the transaction change counter to be | 
| @@ -3501,9 +3859,6 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ | 
| return proxyFileControl(id,op,pArg); | 
| } | 
| #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ | 
| - case SQLITE_FCNTL_SYNC_OMITTED: { | 
| - return SQLITE_OK; /* A no-op */ | 
| - } | 
| } | 
| return SQLITE_NOTFOUND; | 
| } | 
| @@ -3518,21 +3873,138 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ | 
| ** a database and its journal file) that the sector size will be the | 
| ** same for both. | 
| */ | 
| +#ifndef __QNXNTO__ | 
| static int unixSectorSize(sqlite3_file *NotUsed){ | 
| UNUSED_PARAMETER(NotUsed); | 
| return SQLITE_DEFAULT_SECTOR_SIZE; | 
| } | 
| +#endif | 
| /* | 
| -** Return the device characteristics for the file. This is always 0 for unix. | 
| +** The following version of unixSectorSize() is optimized for QNX. | 
| */ | 
| -static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ | 
| - UNUSED_PARAMETER(NotUsed); | 
| - return 0; | 
| +#ifdef __QNXNTO__ | 
| +#include <sys/dcmd_blk.h> | 
| +#include <sys/statvfs.h> | 
| +static int unixSectorSize(sqlite3_file *id){ | 
| + unixFile *pFile = (unixFile*)id; | 
| + if( pFile->sectorSize == 0 ){ | 
| + struct statvfs fsInfo; | 
| + | 
| + /* Set defaults for non-supported filesystems */ | 
| + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; | 
| + pFile->deviceCharacteristics = 0; | 
| + if( fstatvfs(pFile->h, &fsInfo) == -1 ) { | 
| + return pFile->sectorSize; | 
| + } | 
| + | 
| + if( !strcmp(fsInfo.f_basetype, "tmp") ) { | 
| + pFile->sectorSize = fsInfo.f_bsize; | 
| + pFile->deviceCharacteristics = | 
| + SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ | 
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until | 
| + ** the write succeeds */ | 
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind | 
| + ** so it is ordered */ | 
| + 0; | 
| + }else if( strstr(fsInfo.f_basetype, "etfs") ){ | 
| + pFile->sectorSize = fsInfo.f_bsize; | 
| + pFile->deviceCharacteristics = | 
| + /* etfs cluster size writes are atomic */ | 
| + (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | | 
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until | 
| + ** the write succeeds */ | 
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind | 
| + ** so it is ordered */ | 
| + 0; | 
| + }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ | 
| + pFile->sectorSize = fsInfo.f_bsize; | 
| + pFile->deviceCharacteristics = | 
| + SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ | 
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until | 
| + ** the write succeeds */ | 
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind | 
| + ** so it is ordered */ | 
| + 0; | 
| + }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ | 
| + pFile->sectorSize = fsInfo.f_bsize; | 
| + pFile->deviceCharacteristics = | 
| + /* full bitset of atomics from max sector size and smaller */ | 
| + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | | 
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind | 
| + ** so it is ordered */ | 
| + 0; | 
| + }else if( strstr(fsInfo.f_basetype, "dos") ){ | 
| + pFile->sectorSize = fsInfo.f_bsize; | 
| + pFile->deviceCharacteristics = | 
| + /* full bitset of atomics from max sector size and smaller */ | 
| + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | | 
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind | 
| + ** so it is ordered */ | 
| + 0; | 
| + }else{ | 
| + pFile->deviceCharacteristics = | 
| + SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ | 
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until | 
| + ** the write succeeds */ | 
| + 0; | 
| + } | 
| + } | 
| + /* Last chance verification. If the sector size isn't a multiple of 512 | 
| + ** then it isn't valid.*/ | 
| + if( pFile->sectorSize % 512 != 0 ){ | 
| + pFile->deviceCharacteristics = 0; | 
| + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; | 
| + } | 
| + return pFile->sectorSize; | 
| +} | 
| +#endif /* __QNXNTO__ */ | 
| + | 
| +/* | 
| +** Return the device characteristics for the file. | 
| +** | 
| +** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. | 
| +** However, that choice is controversial since technically the underlying | 
| +** file system does not always provide powersafe overwrites. (In other | 
| +** words, after a power-loss event, parts of the file that were never | 
| +** written might end up being altered.) However, non-PSOW behavior is very, | 
| +** very rare. And asserting PSOW makes a large reduction in the amount | 
| +** of required I/O for journaling, since a lot of padding is eliminated. | 
| +** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control | 
| +** available to turn it off and URI query parameter available to turn it off. | 
| +*/ | 
| +static int unixDeviceCharacteristics(sqlite3_file *id){ | 
| + unixFile *p = (unixFile*)id; | 
| + int rc = 0; | 
| +#ifdef __QNXNTO__ | 
| + if( p->sectorSize==0 ) unixSectorSize(id); | 
| + rc = p->deviceCharacteristics; | 
| +#endif | 
| + if( p->ctrlFlags & UNIXFILE_PSOW ){ | 
| + rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; | 
| + } | 
| + return rc; | 
| } | 
| -#ifndef SQLITE_OMIT_WAL | 
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 | 
| + | 
| +/* | 
| +** Return the system page size. | 
| +** | 
| +** This function should not be called directly by other code in this file. | 
| +** Instead, it should be called via macro osGetpagesize(). | 
| +*/ | 
| +static int unixGetpagesize(void){ | 
| +#if defined(_BSD_SOURCE) | 
| + return getpagesize(); | 
| +#else | 
| + return (int)sysconf(_SC_PAGESIZE); | 
| +#endif | 
| +} | 
| + | 
| +#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ | 
| +#ifndef SQLITE_OMIT_WAL | 
| /* | 
| ** Object used to represent an shared memory buffer. | 
| @@ -3569,7 +4041,8 @@ struct unixShmNode { | 
| char *zFilename; /* Name of the mmapped file */ | 
| int h; /* Open file descriptor */ | 
| int szRegion; /* Size of shared-memory regions */ | 
| - int nRegion; /* Size of array apRegion */ | 
| + u16 nRegion; /* Size of array apRegion */ | 
| + u8 isReadonly; /* True if read-only */ | 
| char **apRegion; /* Array of mapped shared-memory regions */ | 
| int nRef; /* Number of unixShm objects pointing to this */ | 
| unixShm *pFirst; /* All unixShm objects pointing to this */ | 
| @@ -3597,11 +4070,9 @@ struct unixShm { | 
| unixShmNode *pShmNode; /* The underlying unixShmNode object */ | 
| unixShm *pNext; /* Next unixShm with the same unixShmNode */ | 
| u8 hasMutex; /* True if holding the unixShmNode mutex */ | 
| + u8 id; /* Id of this connection within its unixShmNode */ | 
| u16 sharedMask; /* Mask of shared locks held */ | 
| u16 exclMask; /* Mask of exclusive locks held */ | 
| -#ifdef SQLITE_DEBUG | 
| - u8 id; /* Id of this connection within its unixShmNode */ | 
| -#endif | 
| }; | 
| /* | 
| @@ -3650,7 +4121,7 @@ static int unixShmSystemLock( | 
| #ifdef SQLITE_DEBUG | 
| { u16 mask; | 
| OSTRACE(("SHM-LOCK ")); | 
| - mask = (1<<(ofst+n)) - (1<<ofst); | 
| + mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst); | 
| if( rc==SQLITE_OK ){ | 
| if( lockType==F_UNLCK ){ | 
| OSTRACE(("unlock %d ok", ofst)); | 
| @@ -3684,6 +4155,22 @@ static int unixShmSystemLock( | 
| return rc; | 
| } | 
| +/* | 
| +** Return the minimum number of 32KB shm regions that should be mapped at | 
| +** a time, assuming that each mapping must be an integer multiple of the | 
| +** current system page-size. | 
| +** | 
| +** Usually, this is 1. The exception seems to be systems that are configured | 
| +** to use 64KB pages - in this case each mapping must cover at least two | 
| +** shm regions. | 
| +*/ | 
| +static int unixShmRegionPerMap(void){ | 
| + int shmsz = 32*1024; /* SHM region size */ | 
| + int pgsz = osGetpagesize(); /* System page size */ | 
| + assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */ | 
| + if( pgsz<shmsz ) return 1; | 
| + return pgsz/shmsz; | 
| +} | 
| /* | 
| ** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0. | 
| @@ -3695,12 +4182,13 @@ static void unixShmPurge(unixFile *pFd){ | 
| unixShmNode *p = pFd->pInode->pShmNode; | 
| assert( unixMutexHeld() ); | 
| if( p && p->nRef==0 ){ | 
| + int nShmPerMap = unixShmRegionPerMap(); | 
| int i; | 
| assert( p->pInode==pFd->pInode ); | 
| - if( p->mutex ) sqlite3_mutex_free(p->mutex); | 
| - for(i=0; i<p->nRegion; i++){ | 
| + sqlite3_mutex_free(p->mutex); | 
| + for(i=0; i<p->nRegion; i+=nShmPerMap){ | 
| if( p->h>=0 ){ | 
| - munmap(p->apRegion[i], p->szRegion); | 
| + osMunmap(p->apRegion[i], p->szRegion); | 
| }else{ | 
| sqlite3_free(p->apRegion[i]); | 
| } | 
| @@ -3775,8 +4263,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ | 
| /* Call fstat() to figure out the permissions on the database file. If | 
| ** a new *-shm file is created, an attempt will be made to create it | 
| - ** with the same permissions. The actual permissions the file is created | 
| - ** with are subject to the current umask setting. | 
| + ** with the same permissions. | 
| */ | 
| if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ | 
| rc = SQLITE_IOERR_FSTAT; | 
| @@ -3784,16 +4271,16 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ | 
| } | 
| #ifdef SQLITE_SHM_DIRECTORY | 
| - nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30; | 
| + nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; | 
| #else | 
| - nShmFilename = 5 + (int)strlen(pDbFd->zPath); | 
| + nShmFilename = 6 + (int)strlen(pDbFd->zPath); | 
| #endif | 
| pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); | 
| if( pShmNode==0 ){ | 
| rc = SQLITE_NOMEM; | 
| goto shm_open_err; | 
| } | 
| - memset(pShmNode, 0, sizeof(*pShmNode)); | 
| + memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); | 
| zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1]; | 
| #ifdef SQLITE_SHM_DIRECTORY | 
| sqlite3_snprintf(nShmFilename, zShmFilename, | 
| @@ -3801,6 +4288,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ | 
| (u32)sStat.st_ino, (u32)sStat.st_dev); | 
| #else | 
| sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); | 
| + sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); | 
| #endif | 
| pShmNode->h = -1; | 
| pDbFd->pInode->pShmNode = pShmNode; | 
| @@ -3812,12 +4300,22 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ | 
| } | 
| if( pInode->bProcessLock==0 ){ | 
| - pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, | 
| - (sStat.st_mode & 0777)); | 
| + int openFlags = O_RDWR | O_CREAT; | 
| + if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ | 
| + openFlags = O_RDONLY; | 
| + pShmNode->isReadonly = 1; | 
| + } | 
| + pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777)); | 
| if( pShmNode->h<0 ){ | 
| rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); | 
| goto shm_open_err; | 
| } | 
| + | 
| + /* If this process is running as root, make sure that the SHM file | 
| + ** is owned by the same user that owns the original database. Otherwise, | 
| + ** the original owner will not be able to connect. | 
| + */ | 
| + osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); | 
| /* Check to see if another process is holding the dead-man switch. | 
| ** If not, truncate the file to zero length. | 
| @@ -3895,6 +4393,8 @@ static int unixShmMap( | 
| unixShm *p; | 
| unixShmNode *pShmNode; | 
| int rc = SQLITE_OK; | 
| + int nShmPerMap = unixShmRegionPerMap(); | 
| + int nReqRegion; | 
| /* If the shared-memory file has not yet been opened, open it now. */ | 
| if( pDbFd->pShm==0 ){ | 
| @@ -3910,9 +4410,12 @@ static int unixShmMap( | 
| assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); | 
| assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); | 
| - if( pShmNode->nRegion<=iRegion ){ | 
| + /* Minimum number of regions required to be mapped. */ | 
| + nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; | 
| + | 
| + if( pShmNode->nRegion<nReqRegion ){ | 
| char **apNew; /* New apRegion[] array */ | 
| - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ | 
| + int nByte = nReqRegion*szRegion; /* Minimum required file size */ | 
| struct stat sStat; /* Used by fstat() */ | 
| pShmNode->szRegion = szRegion; | 
| @@ -3930,36 +4433,55 @@ static int unixShmMap( | 
| if( sStat.st_size<nByte ){ | 
| /* The requested memory region does not exist. If bExtend is set to | 
| ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. | 
| - ** | 
| - ** Alternatively, if bExtend is true, use ftruncate() to allocate | 
| - ** the requested memory region. | 
| */ | 
| - if( !bExtend ) goto shmpage_out; | 
| - if( robust_ftruncate(pShmNode->h, nByte) ){ | 
| - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", | 
| - pShmNode->zFilename); | 
| + if( !bExtend ){ | 
| goto shmpage_out; | 
| } | 
| + | 
| + /* Alternatively, if bExtend is true, extend the file. Do this by | 
| + ** writing a single byte to the end of each (OS) page being | 
| + ** allocated or extended. Technically, we need only write to the | 
| + ** last page in order to extend the file. But writing to all new | 
| + ** pages forces the OS to allocate them immediately, which reduces | 
| + ** the chances of SIGBUS while accessing the mapped region later on. | 
| + */ | 
| + else{ | 
| + static const int pgsz = 4096; | 
| + int iPg; | 
| + | 
| + /* Write to the last byte of each newly allocated or extended page */ | 
| + assert( (nByte % pgsz)==0 ); | 
| + for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){ | 
| + if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){ | 
| + const char *zFile = pShmNode->zFilename; | 
| + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); | 
| + goto shmpage_out; | 
| + } | 
| + } | 
| + } | 
| } | 
| } | 
| /* Map the requested memory region into this processes address space. */ | 
| apNew = (char **)sqlite3_realloc( | 
| - pShmNode->apRegion, (iRegion+1)*sizeof(char *) | 
| + pShmNode->apRegion, nReqRegion*sizeof(char *) | 
| ); | 
| if( !apNew ){ | 
| rc = SQLITE_IOERR_NOMEM; | 
| goto shmpage_out; | 
| } | 
| pShmNode->apRegion = apNew; | 
| - while(pShmNode->nRegion<=iRegion){ | 
| + while( pShmNode->nRegion<nReqRegion ){ | 
| + int nMap = szRegion*nShmPerMap; | 
| + int i; | 
| void *pMem; | 
| if( pShmNode->h>=0 ){ | 
| - pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, | 
| - MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion | 
| + pMem = osMmap(0, nMap, | 
| + pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, | 
| + MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion | 
| ); | 
| if( pMem==MAP_FAILED ){ | 
| - rc = SQLITE_IOERR; | 
| + rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); | 
| goto shmpage_out; | 
| } | 
| }else{ | 
| @@ -3970,8 +4492,11 @@ static int unixShmMap( | 
| } | 
| memset(pMem, 0, szRegion); | 
| } | 
| - pShmNode->apRegion[pShmNode->nRegion] = pMem; | 
| - pShmNode->nRegion++; | 
| + | 
| + for(i=0; i<nShmPerMap; i++){ | 
| + pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i]; | 
| + } | 
| + pShmNode->nRegion += nShmPerMap; | 
| } | 
| } | 
| @@ -3981,6 +4506,7 @@ shmpage_out: | 
| }else{ | 
| *pp = 0; | 
| } | 
| + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; | 
| sqlite3_mutex_leave(pShmNode->mutex); | 
| return rc; | 
| } | 
| @@ -4170,6 +4696,227 @@ static int unixShmUnmap( | 
| # define unixShmUnmap 0 | 
| #endif /* #ifndef SQLITE_OMIT_WAL */ | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| +/* | 
| +** If it is currently memory mapped, unmap file pFd. | 
| +*/ | 
| +static void unixUnmapfile(unixFile *pFd){ | 
| + assert( pFd->nFetchOut==0 ); | 
| + if( pFd->pMapRegion ){ | 
| + osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); | 
| + pFd->pMapRegion = 0; | 
| + pFd->mmapSize = 0; | 
| + pFd->mmapSizeActual = 0; | 
| + } | 
| +} | 
| + | 
| +/* | 
| +** Attempt to set the size of the memory mapping maintained by file | 
| +** descriptor pFd to nNew bytes. Any existing mapping is discarded. | 
| +** | 
| +** If successful, this function sets the following variables: | 
| +** | 
| +** unixFile.pMapRegion | 
| +** unixFile.mmapSize | 
| +** unixFile.mmapSizeActual | 
| +** | 
| +** If unsuccessful, an error message is logged via sqlite3_log() and | 
| +** the three variables above are zeroed. In this case SQLite should | 
| +** continue accessing the database using the xRead() and xWrite() | 
| +** methods. | 
| +*/ | 
| +static void unixRemapfile( | 
| + unixFile *pFd, /* File descriptor object */ | 
| + i64 nNew /* Required mapping size */ | 
| +){ | 
| + const char *zErr = "mmap"; | 
| + int h = pFd->h; /* File descriptor open on db file */ | 
| + u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ | 
| + i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ | 
| + u8 *pNew = 0; /* Location of new mapping */ | 
| + int flags = PROT_READ; /* Flags to pass to mmap() */ | 
| + | 
| + assert( pFd->nFetchOut==0 ); | 
| + assert( nNew>pFd->mmapSize ); | 
| + assert( nNew<=pFd->mmapSizeMax ); | 
| + assert( nNew>0 ); | 
| + assert( pFd->mmapSizeActual>=pFd->mmapSize ); | 
| + assert( MAP_FAILED!=0 ); | 
| + | 
| + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; | 
| + | 
| + if( pOrig ){ | 
| +#if HAVE_MREMAP | 
| + i64 nReuse = pFd->mmapSize; | 
| +#else | 
| + const int szSyspage = osGetpagesize(); | 
| + i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); | 
| +#endif | 
| + u8 *pReq = &pOrig[nReuse]; | 
| + | 
| + /* Unmap any pages of the existing mapping that cannot be reused. */ | 
| + if( nReuse!=nOrig ){ | 
| + osMunmap(pReq, nOrig-nReuse); | 
| + } | 
| + | 
| +#if HAVE_MREMAP | 
| + pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); | 
| + zErr = "mremap"; | 
| +#else | 
| + pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); | 
| + if( pNew!=MAP_FAILED ){ | 
| + if( pNew!=pReq ){ | 
| + osMunmap(pNew, nNew - nReuse); | 
| + pNew = 0; | 
| + }else{ | 
| + pNew = pOrig; | 
| + } | 
| + } | 
| +#endif | 
| + | 
| + /* The attempt to extend the existing mapping failed. Free it. */ | 
| + if( pNew==MAP_FAILED || pNew==0 ){ | 
| + osMunmap(pOrig, nReuse); | 
| + } | 
| + } | 
| + | 
| + /* If pNew is still NULL, try to create an entirely new mapping. */ | 
| + if( pNew==0 ){ | 
| + pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); | 
| + } | 
| + | 
| + if( pNew==MAP_FAILED ){ | 
| + pNew = 0; | 
| + nNew = 0; | 
| + unixLogError(SQLITE_OK, zErr, pFd->zPath); | 
| + | 
| + /* If the mmap() above failed, assume that all subsequent mmap() calls | 
| + ** will probably fail too. Fall back to using xRead/xWrite exclusively | 
| + ** in this case. */ | 
| + pFd->mmapSizeMax = 0; | 
| + } | 
| + pFd->pMapRegion = (void *)pNew; | 
| + pFd->mmapSize = pFd->mmapSizeActual = nNew; | 
| +} | 
| + | 
| +/* | 
| +** Memory map or remap the file opened by file-descriptor pFd (if the file | 
| +** is already mapped, the existing mapping is replaced by the new). Or, if | 
| +** there already exists a mapping for this file, and there are still | 
| +** outstanding xFetch() references to it, this function is a no-op. | 
| +** | 
| +** If parameter nByte is non-negative, then it is the requested size of | 
| +** the mapping to create. Otherwise, if nByte is less than zero, then the | 
| +** requested size is the size of the file on disk. The actual size of the | 
| +** created mapping is either the requested size or the value configured | 
| +** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. | 
| +** | 
| +** SQLITE_OK is returned if no error occurs (even if the mapping is not | 
| +** recreated as a result of outstanding references) or an SQLite error | 
| +** code otherwise. | 
| +*/ | 
| +static int unixMapfile(unixFile *pFd, i64 nByte){ | 
| + i64 nMap = nByte; | 
| + int rc; | 
| + | 
| + assert( nMap>=0 || pFd->nFetchOut==0 ); | 
| + if( pFd->nFetchOut>0 ) return SQLITE_OK; | 
| + | 
| + if( nMap<0 ){ | 
| + struct stat statbuf; /* Low-level file information */ | 
| + rc = osFstat(pFd->h, &statbuf); | 
| + if( rc!=SQLITE_OK ){ | 
| + return SQLITE_IOERR_FSTAT; | 
| + } | 
| + nMap = statbuf.st_size; | 
| + } | 
| + if( nMap>pFd->mmapSizeMax ){ | 
| + nMap = pFd->mmapSizeMax; | 
| + } | 
| + | 
| + if( nMap!=pFd->mmapSize ){ | 
| + if( nMap>0 ){ | 
| + unixRemapfile(pFd, nMap); | 
| + }else{ | 
| + unixUnmapfile(pFd); | 
| + } | 
| + } | 
| + | 
| + return SQLITE_OK; | 
| +} | 
| +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ | 
| + | 
| +/* | 
| +** If possible, return a pointer to a mapping of file fd starting at offset | 
| +** iOff. The mapping must be valid for at least nAmt bytes. | 
| +** | 
| +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. | 
| +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. | 
| +** Finally, if an error does occur, return an SQLite error code. The final | 
| +** value of *pp is undefined in this case. | 
| +** | 
| +** If this function does return a pointer, the caller must eventually | 
| +** release the reference by calling unixUnfetch(). | 
| +*/ | 
| +static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ | 
| +#endif | 
| + *pp = 0; | 
| + | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + if( pFd->mmapSizeMax>0 ){ | 
| + if( pFd->pMapRegion==0 ){ | 
| + int rc = unixMapfile(pFd, -1); | 
| + if( rc!=SQLITE_OK ) return rc; | 
| + } | 
| + if( pFd->mmapSize >= iOff+nAmt ){ | 
| + *pp = &((u8 *)pFd->pMapRegion)[iOff]; | 
| + pFd->nFetchOut++; | 
| + } | 
| + } | 
| +#endif | 
| + return SQLITE_OK; | 
| +} | 
| + | 
| +/* | 
| +** If the third argument is non-NULL, then this function releases a | 
| +** reference obtained by an earlier call to unixFetch(). The second | 
| +** argument passed to this function must be the same as the corresponding | 
| +** argument that was passed to the unixFetch() invocation. | 
| +** | 
| +** Or, if the third argument is NULL, then this function is being called | 
| +** to inform the VFS layer that, according to POSIX, any existing mapping | 
| +** may now be invalid and should be unmapped. | 
| +*/ | 
| +static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ | 
| + UNUSED_PARAMETER(iOff); | 
| + | 
| + /* If p==0 (unmap the entire file) then there must be no outstanding | 
| + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), | 
| + ** then there must be at least one outstanding. */ | 
| + assert( (p==0)==(pFd->nFetchOut==0) ); | 
| + | 
| + /* If p!=0, it must match the iOff value. */ | 
| + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); | 
| + | 
| + if( p ){ | 
| + pFd->nFetchOut--; | 
| + }else{ | 
| + unixUnmapfile(pFd); | 
| + } | 
| + | 
| + assert( pFd->nFetchOut>=0 ); | 
| +#else | 
| + UNUSED_PARAMETER(fd); | 
| + UNUSED_PARAMETER(p); | 
| + UNUSED_PARAMETER(iOff); | 
| +#endif | 
| + return SQLITE_OK; | 
| +} | 
| + | 
| /* | 
| ** Here ends the implementation of all sqlite3_file methods. | 
| ** | 
| @@ -4189,7 +4936,7 @@ static int unixShmUnmap( | 
| ** looks at the filesystem type and tries to guess the best locking | 
| ** strategy from that. | 
| ** | 
| -** For finder-funtion F, two objects are created: | 
| +** For finder-function F, two objects are created: | 
| ** | 
| ** (1) The real finder-function named "FImpt()". | 
| ** | 
| @@ -4210,7 +4957,7 @@ static int unixShmUnmap( | 
| ** * An I/O method finder function called FINDER that returns a pointer | 
| ** to the METHOD object in the previous bullet. | 
| */ | 
| -#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \ | 
| +#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK, SHMMAP) \ | 
| static const sqlite3_io_methods METHOD = { \ | 
| VERSION, /* iVersion */ \ | 
| CLOSE, /* xClose */ \ | 
| @@ -4225,10 +4972,12 @@ static const sqlite3_io_methods METHOD = { \ | 
| unixFileControl, /* xFileControl */ \ | 
| unixSectorSize, /* xSectorSize */ \ | 
| unixDeviceCharacteristics, /* xDeviceCapabilities */ \ | 
| - unixShmMap, /* xShmMap */ \ | 
| + SHMMAP, /* xShmMap */ \ | 
| unixShmLock, /* xShmLock */ \ | 
| unixShmBarrier, /* xShmBarrier */ \ | 
| - unixShmUnmap /* xShmUnmap */ \ | 
| + unixShmUnmap, /* xShmUnmap */ \ | 
| + unixFetch, /* xFetch */ \ | 
| + unixUnfetch, /* xUnfetch */ \ | 
| }; \ | 
| static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ | 
| UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ | 
| @@ -4245,20 +4994,22 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ | 
| IOMETHODS( | 
| posixIoFinder, /* Finder function name */ | 
| posixIoMethods, /* sqlite3_io_methods object name */ | 
| - 2, /* shared memory is enabled */ | 
| + 3, /* shared memory and mmap are enabled */ | 
| unixClose, /* xClose method */ | 
| unixLock, /* xLock method */ | 
| unixUnlock, /* xUnlock method */ | 
| - unixCheckReservedLock /* xCheckReservedLock method */ | 
| + unixCheckReservedLock, /* xCheckReservedLock method */ | 
| + unixShmMap /* xShmMap method */ | 
| ) | 
| IOMETHODS( | 
| nolockIoFinder, /* Finder function name */ | 
| nolockIoMethods, /* sqlite3_io_methods object name */ | 
| - 1, /* shared memory is disabled */ | 
| + 3, /* shared memory is disabled */ | 
| nolockClose, /* xClose method */ | 
| nolockLock, /* xLock method */ | 
| nolockUnlock, /* xUnlock method */ | 
| - nolockCheckReservedLock /* xCheckReservedLock method */ | 
| + nolockCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| IOMETHODS( | 
| dotlockIoFinder, /* Finder function name */ | 
| @@ -4267,7 +5018,8 @@ IOMETHODS( | 
| dotlockClose, /* xClose method */ | 
| dotlockLock, /* xLock method */ | 
| dotlockUnlock, /* xUnlock method */ | 
| - dotlockCheckReservedLock /* xCheckReservedLock method */ | 
| + dotlockCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS | 
| @@ -4278,7 +5030,8 @@ IOMETHODS( | 
| flockClose, /* xClose method */ | 
| flockLock, /* xLock method */ | 
| flockUnlock, /* xUnlock method */ | 
| - flockCheckReservedLock /* xCheckReservedLock method */ | 
| + flockCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #endif | 
| @@ -4290,7 +5043,8 @@ IOMETHODS( | 
| semClose, /* xClose method */ | 
| semLock, /* xLock method */ | 
| semUnlock, /* xUnlock method */ | 
| - semCheckReservedLock /* xCheckReservedLock method */ | 
| + semCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #endif | 
| @@ -4302,7 +5056,8 @@ IOMETHODS( | 
| afpClose, /* xClose method */ | 
| afpLock, /* xLock method */ | 
| afpUnlock, /* xUnlock method */ | 
| - afpCheckReservedLock /* xCheckReservedLock method */ | 
| + afpCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #endif | 
| @@ -4327,7 +5082,8 @@ IOMETHODS( | 
| proxyClose, /* xClose method */ | 
| proxyLock, /* xLock method */ | 
| proxyUnlock, /* xUnlock method */ | 
| - proxyCheckReservedLock /* xCheckReservedLock method */ | 
| + proxyCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #endif | 
| @@ -4340,7 +5096,8 @@ IOMETHODS( | 
| unixClose, /* xClose method */ | 
| unixLock, /* xLock method */ | 
| nfsUnlock, /* xUnlock method */ | 
| - unixCheckReservedLock /* xCheckReservedLock method */ | 
| + unixCheckReservedLock, /* xCheckReservedLock method */ | 
| + 0 /* xShmMap method */ | 
| ) | 
| #endif | 
| @@ -4449,7 +5206,7 @@ static const sqlite3_io_methods | 
| #endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ | 
| /* | 
| -** An abstract type for a pointer to a IO method finder function: | 
| +** An abstract type for a pointer to an IO method finder function: | 
| */ | 
| typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); | 
| @@ -4462,24 +5219,14 @@ typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); | 
| */ | 
| /* | 
| -** Initializes a unixFile structure with zeros. | 
| -*/ | 
| -void initUnixFile(sqlite3_file* file) { | 
| - memset(file, 0, sizeof(unixFile)); | 
| -} | 
| - | 
| -/* | 
| ** Initialize the contents of the unixFile structure pointed to by pId. | 
| */ | 
| -int fillInUnixFile( | 
| +static int fillInUnixFile( | 
| sqlite3_vfs *pVfs, /* Pointer to vfs object */ | 
| int h, /* Open file descriptor of file being opened */ | 
| - int syncDir, /* True to sync directory on first sync */ | 
| sqlite3_file *pId, /* Write to the unixFile structure here */ | 
| const char *zFilename, /* Name of the file being opened */ | 
| - int noLock, /* Omit locking if true */ | 
| - int isDelete, /* Delete on close if true */ | 
| - int isReadOnly /* True if the file is opened read-only */ | 
| + int ctrlFlags /* Zero or more UNIXFILE_* values */ | 
| ){ | 
| const sqlite3_io_methods *pLockingStyle; | 
| unixFile *pNew = (unixFile *)pId; | 
| @@ -4487,11 +5234,6 @@ int fillInUnixFile( | 
| assert( pNew->pInode==NULL ); | 
| - /* Parameter isDelete is only used on vxworks. Express this explicitly | 
| - ** here to prevent compiler warnings about unused parameters. | 
| - */ | 
| - UNUSED_PARAMETER(isDelete); | 
| - | 
| /* Usually the path zFilename should not be a relative pathname. The | 
| ** exception is when opening the proxy "conch" file in builds that | 
| ** include the special Apple locking styles. | 
| @@ -4503,30 +5245,34 @@ int fillInUnixFile( | 
| assert( zFilename==0 || zFilename[0]=='/' ); | 
| #endif | 
| + /* No locking occurs in temporary files */ | 
| + assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); | 
| + | 
| OSTRACE(("OPEN %-3d %s\n", h, zFilename)); | 
| pNew->h = h; | 
| + pNew->pVfs = pVfs; | 
| pNew->zPath = zFilename; | 
| - if( strcmp(pVfs->zName,"unix-excl")==0 ){ | 
| - pNew->ctrlFlags = UNIXFILE_EXCL; | 
| - }else{ | 
| - pNew->ctrlFlags = 0; | 
| - } | 
| - if( isReadOnly ){ | 
| - pNew->ctrlFlags |= UNIXFILE_RDONLY; | 
| + pNew->ctrlFlags = (u8)ctrlFlags; | 
| +#if SQLITE_MAX_MMAP_SIZE>0 | 
| + pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap; | 
| +#endif | 
| + if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), | 
| + "psow", SQLITE_POWERSAFE_OVERWRITE) ){ | 
| + pNew->ctrlFlags |= UNIXFILE_PSOW; | 
| } | 
| - if( syncDir ){ | 
| - pNew->ctrlFlags |= UNIXFILE_DIRSYNC; | 
| + if( strcmp(pVfs->zName,"unix-excl")==0 ){ | 
| + pNew->ctrlFlags |= UNIXFILE_EXCL; | 
| } | 
| #if OS_VXWORKS | 
| pNew->pId = vxworksFindFileId(zFilename); | 
| if( pNew->pId==0 ){ | 
| - noLock = 1; | 
| + ctrlFlags |= UNIXFILE_NOLOCK; | 
| rc = SQLITE_NOMEM; | 
| } | 
| #endif | 
| - if( noLock ){ | 
| + if( ctrlFlags & UNIXFILE_NOLOCK ){ | 
| pLockingStyle = &nolockIoMethods; | 
| }else{ | 
| pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); | 
| @@ -4546,7 +5292,7 @@ int fillInUnixFile( | 
| unixEnterMutex(); | 
| rc = findInodeInfo(pNew, &pNew->pInode); | 
| if( rc!=SQLITE_OK ){ | 
| - /* If an error occured in findInodeInfo(), close the file descriptor | 
| + /* If an error occurred in findInodeInfo(), close the file descriptor | 
| ** immediately, before releasing the mutex. findInodeInfo() may fail | 
| ** in two scenarios: | 
| ** | 
| @@ -4604,6 +5350,7 @@ int fillInUnixFile( | 
| */ | 
| char *zLockFile; | 
| int nFilename; | 
| + assert( zFilename!=0 ); | 
| nFilename = (int)strlen(zFilename) + 6; | 
| zLockFile = (char *)sqlite3_malloc(nFilename); | 
| if( zLockFile==0 ){ | 
| @@ -4644,15 +5391,15 @@ int fillInUnixFile( | 
| if( h>=0 ) robust_close(pNew, h, __LINE__); | 
| h = -1; | 
| osUnlink(zFilename); | 
| - isDelete = 0; | 
| + pNew->ctrlFlags |= UNIXFILE_DELETE; | 
| } | 
| - pNew->isDelete = isDelete; | 
| #endif | 
| if( rc!=SQLITE_OK ){ | 
| if( h>=0 ) robust_close(pNew, h, __LINE__); | 
| }else{ | 
| pNew->pMethod = pLockingStyle; | 
| OpenCounter(+1); | 
| + verifyDbFile(pNew); | 
| } | 
| return rc; | 
| } | 
| @@ -4665,6 +5412,7 @@ static const char *unixTempFileDir(void){ | 
| static const char *azDirs[] = { | 
| 0, | 
| 0, | 
| + 0, | 
| "/var/tmp", | 
| "/usr/tmp", | 
| "/tmp", | 
| @@ -4675,7 +5423,8 @@ static const char *unixTempFileDir(void){ | 
| const char *zDir = 0; | 
| azDirs[0] = sqlite3_temp_directory; | 
| - if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); | 
| + if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR"); | 
| + if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR"); | 
| for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ | 
| if( zDir==0 ) continue; | 
| if( osStat(zDir, &buf) ) continue; | 
| @@ -4711,18 +5460,19 @@ static int unixGetTempname(int nBuf, char *zBuf){ | 
| /* Check that the output buffer is large enough for the temporary file | 
| ** name. If it is not, return SQLITE_ERROR. | 
| */ | 
| - if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){ | 
| + if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){ | 
| return SQLITE_ERROR; | 
| } | 
| do{ | 
| - sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); | 
| + sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); | 
| j = (int)strlen(zBuf); | 
| sqlite3_randomness(15, &zBuf[j]); | 
| for(i=0; i<15; i++, j++){ | 
| zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; | 
| } | 
| zBuf[j] = 0; | 
| + zBuf[j+1] = 0; | 
| }while( osAccess(zBuf,0)==0 ); | 
| return SQLITE_OK; | 
| } | 
| @@ -4770,7 +5520,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ | 
| ** descriptor on the same path, fail, and return an error to SQLite. | 
| ** | 
| ** Even if a subsequent open() call does succeed, the consequences of | 
| - ** not searching for a resusable file descriptor are not dire. */ | 
| + ** not searching for a reusable file descriptor are not dire. */ | 
| if( 0==osStat(zPath, &sStat) ){ | 
| unixInodeInfo *pInode; | 
| @@ -4801,23 +5551,31 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ | 
| ** written to *pMode. If an IO error occurs, an SQLite error code is | 
| ** returned and the value of *pMode is not modified. | 
| ** | 
| -** If the file being opened is a temporary file, it is always created with | 
| -** the octal permissions 0600 (read/writable by owner only). If the file | 
| -** is a database or master journal file, it is created with the permissions | 
| -** mask SQLITE_DEFAULT_FILE_PERMISSIONS. | 
| -** | 
| -** Finally, if the file being opened is a WAL or regular journal file, then | 
| +** In most cases, this routine sets *pMode to 0, which will become | 
| +** an indication to robust_open() to create the file using | 
| +** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask. | 
| +** But if the file being opened is a WAL or regular journal file, then | 
| ** this function queries the file-system for the permissions on the | 
| ** corresponding database file and sets *pMode to this value. Whenever | 
| ** possible, WAL and journal files are created using the same permissions | 
| ** as the associated database file. | 
| +** | 
| +** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the | 
| +** original filename is unavailable. But 8_3_NAMES is only used for | 
| +** FAT filesystems and permissions do not matter there, so just use | 
| +** the default permissions. | 
| */ | 
| static int findCreateFileMode( | 
| const char *zPath, /* Path of file (possibly) being created */ | 
| int flags, /* Flags passed as 4th argument to xOpen() */ | 
| - mode_t *pMode /* OUT: Permissions to open file with */ | 
| + mode_t *pMode, /* OUT: Permissions to open file with */ | 
| + uid_t *pUid, /* OUT: uid to set on the file */ | 
| + gid_t *pGid /* OUT: gid to set on the file */ | 
| ){ | 
| int rc = SQLITE_OK; /* Return Code */ | 
| + *pMode = 0; | 
| + *pUid = 0; | 
| + *pGid = 0; | 
| if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ | 
| char zDb[MAX_PATHNAME+1]; /* Database file path */ | 
| int nDb; /* Number of valid bytes in zDb */ | 
| @@ -4829,27 +5587,35 @@ static int findCreateFileMode( | 
| ** | 
| ** "<path to db>-journal" | 
| ** "<path to db>-wal" | 
| - ** "<path to db>-journal-NNNN" | 
| - ** "<path to db>-wal-NNNN" | 
| + ** "<path to db>-journalNN" | 
| + ** "<path to db>-walNN" | 
| ** | 
| - ** where NNNN is a 4 digit decimal number. The NNNN naming schemes are | 
| + ** where NN is a decimal number. The NN naming schemes are | 
| ** used by the test_multiplex.c module. | 
| */ | 
| nDb = sqlite3Strlen30(zPath) - 1; | 
| - while( nDb>0 && zPath[nDb]!='l' ) nDb--; | 
| - nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7); | 
| +#ifdef SQLITE_ENABLE_8_3_NAMES | 
| + while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--; | 
| + if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK; | 
| +#else | 
| + while( zPath[nDb]!='-' ){ | 
| + assert( nDb>0 ); | 
| + assert( zPath[nDb]!='\n' ); | 
| + nDb--; | 
| + } | 
| +#endif | 
| memcpy(zDb, zPath, nDb); | 
| zDb[nDb] = '\0'; | 
| if( 0==osStat(zDb, &sStat) ){ | 
| *pMode = sStat.st_mode & 0777; | 
| + *pUid = sStat.st_uid; | 
| + *pGid = sStat.st_gid; | 
| }else{ | 
| rc = SQLITE_IOERR_FSTAT; | 
| } | 
| }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ | 
| *pMode = 0600; | 
| - }else{ | 
| - *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; | 
| } | 
| return rc; | 
| } | 
| @@ -4866,9 +5632,9 @@ int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* vfs, | 
| int dirfd, | 
| sqlite3_file* file, | 
| const char* fileName, | 
| - int noLock, | 
| - int isDelete) { | 
| - return fillInUnixFile(vfs, fd, dirfd, file, fileName, noLock, isDelete, 0); | 
| + int noLock) { | 
| + int ctrlFlags = (noLock ? UNIXFILE_NOLOCK : 0); | 
| + return fillInUnixFile(vfs, fd, file, fileName, ctrlFlags); | 
| } | 
| /* | 
| @@ -4956,6 +5722,7 @@ static int unixOpen( | 
| int eType = flags&0xFFFFFF00; /* Type of file to open */ | 
| int noLock; /* True to omit locking primitives */ | 
| int rc = SQLITE_OK; /* Function Return Code */ | 
| + int ctrlFlags = 0; /* UNIXFILE_* flags */ | 
| int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); | 
| int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); | 
| @@ -4965,6 +5732,9 @@ static int unixOpen( | 
| #if SQLITE_ENABLE_LOCKING_STYLE | 
| int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); | 
| #endif | 
| +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE | 
| + struct statfs fsInfo; | 
| +#endif | 
| /* If creating a master or main-file journal, this function will open | 
| ** a file-descriptor on the directory too. The first time unixSync() | 
| @@ -4979,7 +5749,7 @@ static int unixOpen( | 
| /* If argument zPath is a NULL pointer, this function is required to open | 
| ** a temporary file. Use this buffer to store the file name in. | 
| */ | 
| - char zTmpname[MAX_PATHNAME+1]; | 
| + char zTmpname[MAX_PATHNAME+2]; | 
| const char *zName = zPath; | 
| /* Check the following statements are true: | 
| @@ -5008,6 +5778,16 @@ static int unixOpen( | 
| || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL | 
| ); | 
| + /* Detect a pid change and reset the PRNG. There is a race condition | 
| + ** here such that two or more threads all trying to open databases at | 
| + ** the same instant might all reset the PRNG. But multiple resets | 
| + ** are harmless. | 
| + */ | 
| + if( randomnessPid!=getpid() ){ | 
| + randomnessPid = getpid(); | 
| + sqlite3_randomness(0,0); | 
| + } | 
| + | 
| chromium_sqlite3_initialize_unix_sqlite3_file(pFile); | 
| if( eType==SQLITE_OPEN_MAIN_DB ){ | 
| @@ -5015,14 +5795,24 @@ static int unixOpen( | 
| if( rc!=SQLITE_OK ){ | 
| return rc; | 
| } | 
| + | 
| + /* Database filenames are double-zero terminated if they are not | 
| + ** URIs with parameters. Hence, they can always be passed into | 
| + ** sqlite3_uri_parameter(). */ | 
| + assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); | 
| + | 
| }else if( !zName ){ | 
| /* If zName is NULL, the upper layer is requesting a temp file. */ | 
| assert(isDelete && !syncDir); | 
| - rc = unixGetTempname(MAX_PATHNAME+1, zTmpname); | 
| + rc = unixGetTempname(MAX_PATHNAME+2, zTmpname); | 
| if( rc!=SQLITE_OK ){ | 
| return rc; | 
| } | 
| zName = zTmpname; | 
| + | 
| + /* Generated temporary filenames are always double-zero terminated | 
| + ** for use by sqlite3_uri_parameter(). */ | 
| + assert( zName[strlen(zName)+1]==0 ); | 
| } | 
| /* Determine the value of the flags parameter passed to POSIX function | 
| @@ -5037,7 +5827,9 @@ static int unixOpen( | 
| if( fd<0 ){ | 
| mode_t openMode; /* Permissions to create file with */ | 
| - rc = findCreateFileMode(zName, flags, &openMode); | 
| + uid_t uid; /* Userid for the file */ | 
| + gid_t gid; /* Groupid for the file */ | 
| + rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); | 
| if( rc!=SQLITE_OK ){ | 
| assert( !p->pUnused ); | 
| assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); | 
| @@ -5058,6 +5850,14 @@ static int unixOpen( | 
| rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); | 
| goto open_finished; | 
| } | 
| + | 
| + /* If this process is running as root and if creating a new rollback | 
| + ** journal or WAL file, set the ownership of the journal or WAL to be | 
| + ** the same as the original database. | 
| + */ | 
| + if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ | 
| + osFchown(fd, uid, gid); | 
| + } | 
| } | 
| assert( fd>=0 ); | 
| if( pOutFlags ){ | 
| @@ -5069,6 +5869,12 @@ static int unixOpen( | 
| if( isDelete ){ | 
| #if OS_VXWORKS | 
| zPath = zName; | 
| +#elif defined(SQLITE_UNLINK_AFTER_CLOSE) | 
| + zPath = sqlite3_mprintf("%s", zName); | 
| + if( zPath==0 ){ | 
| + robust_close(p, fd, __LINE__); | 
| + return SQLITE_NOMEM; | 
| + } | 
| #else | 
| osUnlink(zName); | 
| #endif | 
| @@ -5079,15 +5885,10 @@ static int unixOpen( | 
| } | 
| #endif | 
| -#ifdef FD_CLOEXEC | 
| - osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | 
| -#endif | 
| - | 
| noLock = eType!=SQLITE_OPEN_MAIN_DB; | 
| #if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE | 
| - struct statfs fsInfo; | 
| if( fstatfs(fd, &fsInfo) == -1 ){ | 
| ((unixFile*)pFile)->lastErrno = errno; | 
| robust_close(p, fd, __LINE__); | 
| @@ -5097,7 +5898,14 @@ static int unixOpen( | 
| ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; | 
| } | 
| #endif | 
| - | 
| + | 
| + /* Set up appropriate ctrlFlags */ | 
| + if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; | 
| + if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; | 
| + if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; | 
| + if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC; | 
| + if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; | 
| + | 
| #if SQLITE_ENABLE_LOCKING_STYLE | 
| #if SQLITE_PREFER_PROXY_LOCKING | 
| isAutoProxy = 1; | 
| @@ -5111,7 +5919,6 @@ static int unixOpen( | 
| if( envforce!=NULL ){ | 
| useProxy = atoi(envforce)>0; | 
| }else{ | 
| - struct statfs fsInfo; | 
| if( statfs(zPath, &fsInfo) == -1 ){ | 
| /* In theory, the close(fd) call is sub-optimal. If the file opened | 
| ** with fd is a database file, and there are other connections open | 
| @@ -5128,8 +5935,7 @@ static int unixOpen( | 
| useProxy = !(fsInfo.f_flags&MNT_LOCAL); | 
| } | 
| if( useProxy ){ | 
| - rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock, | 
| - isDelete, isReadonly); | 
| + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); | 
| if( rc==SQLITE_OK ){ | 
| rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); | 
| if( rc!=SQLITE_OK ){ | 
| @@ -5146,8 +5952,8 @@ static int unixOpen( | 
| } | 
| #endif | 
| - rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock, | 
| - isDelete, isReadonly); | 
| + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); | 
| + | 
| open_finished: | 
| if( rc!=SQLITE_OK ){ | 
| chromium_sqlite3_destroy_reusable_file_handle(pFile); | 
| @@ -5168,11 +5974,20 @@ static int unixDelete( | 
| int rc = SQLITE_OK; | 
| UNUSED_PARAMETER(NotUsed); | 
| SimulateIOError(return SQLITE_IOERR_DELETE); | 
| - if( osUnlink(zPath)==(-1) && errno!=ENOENT ){ | 
| - return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); | 
| + if( osUnlink(zPath)==(-1) ){ | 
| + if( errno==ENOENT | 
| +#if OS_VXWORKS | 
| + || osAccess(zPath,0)!=0 | 
| +#endif | 
| + ){ | 
| + rc = SQLITE_IOERR_DELETE_NOENT; | 
| + }else{ | 
| + rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); | 
| + } | 
| + return rc; | 
| } | 
| #ifndef SQLITE_DISABLE_DIRSYNC | 
| - if( dirSync ){ | 
| + if( (dirSync & 1)!=0 ){ | 
| int fd; | 
| rc = osOpenDirectory(zPath, &fd); | 
| if( rc==SQLITE_OK ){ | 
| @@ -5194,7 +6009,7 @@ static int unixDelete( | 
| } | 
| /* | 
| -** Test the existance of or access permissions of file zPath. The | 
| +** Test the existence of or access permissions of file zPath. The | 
| ** test performed depends on the value of flags: | 
| ** | 
| ** SQLITE_ACCESS_EXISTS: Return 1 if the file exists | 
| @@ -5360,20 +6175,20 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ | 
| ** tests repeatable. | 
| */ | 
| memset(zBuf, 0, nBuf); | 
| + randomnessPid = getpid(); | 
| #if !defined(SQLITE_TEST) | 
| { | 
| - int pid, fd; | 
| + int fd, got; | 
| fd = robust_open("/dev/urandom", O_RDONLY, 0); | 
| if( fd<0 ){ | 
| time_t t; | 
| time(&t); | 
| memcpy(zBuf, &t, sizeof(t)); | 
| - pid = getpid(); | 
| - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); | 
| - assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); | 
| - nBuf = sizeof(t) + sizeof(pid); | 
| + memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); | 
| + assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); | 
| + nBuf = sizeof(t) + sizeof(randomnessPid); | 
| }else{ | 
| - do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); | 
| + do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); | 
| robust_close(0, fd, __LINE__); | 
| } | 
| } | 
| @@ -5427,10 +6242,12 @@ int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ | 
| ** epoch of noon in Greenwich on November 24, 4714 B.C according to the | 
| ** proleptic Gregorian calendar. | 
| ** | 
| -** On success, return 0. Return 1 if the time and date cannot be found. | 
| +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date | 
| +** cannot be found. | 
| */ | 
| static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ | 
| static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; | 
| + int rc = SQLITE_OK; | 
| #if defined(NO_GETTOD) | 
| time_t t; | 
| time(&t); | 
| @@ -5441,8 +6258,11 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ | 
| *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; | 
| #else | 
| struct timeval sNow; | 
| - gettimeofday(&sNow, 0); | 
| - *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; | 
| + if( gettimeofday(&sNow, 0)==0 ){ | 
| + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; | 
| + }else{ | 
| + rc = SQLITE_ERROR; | 
| + } | 
| #endif | 
| #ifdef SQLITE_TEST | 
| @@ -5451,7 +6271,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ | 
| } | 
| #endif | 
| UNUSED_PARAMETER(NotUsed); | 
| - return 0; | 
| + return rc; | 
| } | 
| /* | 
| @@ -5460,11 +6280,12 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ | 
| ** return 0. Return 1 if the time and date cannot be found. | 
| */ | 
| static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ | 
| - sqlite3_int64 i; | 
| + sqlite3_int64 i = 0; | 
| + int rc; | 
| UNUSED_PARAMETER(NotUsed); | 
| - unixCurrentTimeInt64(0, &i); | 
| + rc = unixCurrentTimeInt64(0, &i); | 
| *prNow = i/86400000.0; | 
| - return 0; | 
| + return rc; | 
| } | 
| /* | 
| @@ -5510,7 +6331,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ | 
| ** address in the shared range is taken for a SHARED lock, the entire | 
| ** shared range is taken for an EXCLUSIVE lock): | 
| ** | 
| -** PENDING_BYTE 0x40000000 | 
| +** PENDING_BYTE 0x40000000 | 
| ** RESERVED_BYTE 0x40000001 | 
| ** SHARED_RANGE 0x40000002 -> 0x40000200 | 
| ** | 
| @@ -5579,7 +6400,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ | 
| ** proxy path against the values stored in the conch. The conch file is | 
| ** stored in the same directory as the database file and the file name | 
| ** is patterned after the database file name as ".<databasename>-conch". | 
| -** If the conch file does not exist, or it's contents do not match the | 
| +** If the conch file does not exist, or its contents do not match the | 
| ** host ID and/or proxy path, then the lock is escalated to an exclusive | 
| ** lock and the conch file contents is updated with the host ID and proxy | 
| ** path and the lock is downgraded to a shared lock again. If the conch | 
| @@ -5631,7 +6452,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ | 
| ** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will | 
| ** force proxy locking to be used for every database file opened, and 0 | 
| ** will force automatic proxy locking to be disabled for all database | 
| -** files (explicity calling the SQLITE_SET_LOCKPROXYFILE pragma or | 
| +** files (explicitly calling the SQLITE_SET_LOCKPROXYFILE pragma or | 
| ** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). | 
| */ | 
| @@ -5717,7 +6538,7 @@ static int proxyCreateLockPath(const char *lockPath){ | 
| if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') | 
| || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ | 
| buf[i]='\0'; | 
| - if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ | 
| + if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ | 
| int err=errno; | 
| if( err!=EEXIST ) { | 
| OSTRACE(("CREATELOCKPATH FAILED creating %s, " | 
| @@ -5771,17 +6592,17 @@ static int proxyCreateUnixFile( | 
| } | 
| } | 
| if( fd<0 ){ | 
| - fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); | 
| + fd = robust_open(path, openFlags, 0); | 
| terrno = errno; | 
| if( fd<0 && errno==ENOENT && islockfile ){ | 
| if( proxyCreateLockPath(path) == SQLITE_OK ){ | 
| - fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); | 
| + fd = robust_open(path, openFlags, 0); | 
| } | 
| } | 
| } | 
| if( fd<0 ){ | 
| openFlags = O_RDONLY; | 
| - fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); | 
| + fd = robust_open(path, openFlags, 0); | 
| terrno = errno; | 
| } | 
| if( fd<0 ){ | 
| @@ -5812,7 +6633,7 @@ static int proxyCreateUnixFile( | 
| pUnused->flags = openFlags; | 
| pNew->pUnused = pUnused; | 
| - rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0); | 
| + rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0); | 
| if( rc==SQLITE_OK ){ | 
| *ppFile = pNew; | 
| return SQLITE_OK; | 
| @@ -5852,6 +6673,8 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){ | 
| return SQLITE_IOERR; | 
| } | 
| } | 
| +#else | 
| + UNUSED_PARAMETER(pError); | 
| #endif | 
| #ifdef SQLITE_TEST | 
| /* simulate multiple hosts by creating unique hostid file paths */ | 
| @@ -5903,8 +6726,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ | 
| goto end_breaklock; | 
| } | 
| /* write it out to the temporary break file */ | 
| - fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), | 
| - SQLITE_DEFAULT_FILE_PERMISSIONS); | 
| + fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0); | 
| if( fd<0 ){ | 
| sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno); | 
| goto end_breaklock; | 
| @@ -5944,6 +6766,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ | 
| int nTries = 0; | 
| struct timespec conchModTime; | 
| + memset(&conchModTime, 0, sizeof(conchModTime)); | 
| do { | 
| rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); | 
| nTries ++; | 
| @@ -6175,12 +6998,12 @@ static int proxyTakeConch(unixFile *pFile){ | 
| end_takeconch: | 
| OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); | 
| if( rc==SQLITE_OK && pFile->openFlags ){ | 
| + int fd; | 
| if( pFile->h>=0 ){ | 
| robust_close(pFile, pFile->h, __LINE__); | 
| } | 
| pFile->h = -1; | 
| - int fd = robust_open(pCtx->dbPath, pFile->openFlags, | 
| - SQLITE_DEFAULT_FILE_PERMISSIONS); | 
| + fd = robust_open(pCtx->dbPath, pFile->openFlags, 0); | 
| OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); | 
| if( fd>=0 ){ | 
| pFile->h = fd; | 
| @@ -6749,7 +7572,7 @@ int sqlite3_os_init(void){ | 
| /* Double-check that the aSyscall[] array has been constructed | 
| ** correctly. See ticket [bb3a86e890c8e96ab] */ | 
| - assert( ArraySize(aSyscall)==18 ); | 
| + assert( ArraySize(aSyscall)==25 ); | 
| /* Register all VFSes defined in the aVfs[] array */ | 
| for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |