Chromium Code Reviews| 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++){ |