diff options
Diffstat (limited to 'WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp')
-rw-r--r-- | WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp | 1081 |
1 files changed, 1036 insertions, 45 deletions
diff --git a/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp b/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp index 6549936..1102df5 100644 --- a/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp +++ b/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp @@ -34,23 +34,938 @@ #include "ChromiumBridge.h" #include <sqlite3.h> +#include <errno.h> #include <fcntl.h> #include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> #include <unistd.h> using namespace WebCore; -// Defined in Chromium's codebase in third_party/sqlite/src/os_unix.c -extern "C" { -void chromium_sqlite3_initialize_unix_sqlite3_file(sqlite3_file* file); -int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* vfs, int fd, int dirfd, sqlite3_file* file, const char* fileName, int noLock); -int chromium_sqlite3_get_reusable_file_handle(sqlite3_file* file, const char* fileName, int flags, int* fd); -void chromium_sqlite3_update_reusable_file_handle(sqlite3_file* file, int fd, int flags); -void chromium_sqlite3_destroy_reusable_file_handle(sqlite3_file* file); +// Chromium's Posix implementation of SQLite VFS. +// This is heavily based on SQLite's os_unix.c, +// without parts we don't need. + +// Identifies a file by its device number and inode. +struct ChromiumFileId { + dev_t dev; // Device number. + ino_t ino; // Inode number. +}; + +// Information about file locks (one per open inode). Note that many open +// file descriptors may refer to the same inode. +struct ChromiumLockInfo { + ChromiumFileId lockKey; // File identifier. + int cnt; // Number of shared locks held. + int locktype; // Type of the lock. + int nRef; // Reference count. + + // Double-linked list pointers. + ChromiumLockInfo* pNext; + ChromiumLockInfo* pPrev; +}; + +// Information about a file descriptor that cannot be closed immediately. +struct ChromiumUnusedFd { + int fd; // File descriptor. + int flags; // Flags this file descriptor was opened with. + ChromiumUnusedFd* pNext; // Next unused file descriptor on the same file. +}; + +// Information about an open inode. When we want to close an inode +// that still has locks, we defer the close until all locks are cleared. +struct ChromiumOpenInfo { + ChromiumFileId fileId; // The lookup key. + int nRef; // Reference count. + int nLock; // Number of outstanding locks. + ChromiumUnusedFd* pUnused; // List of file descriptors to close. + + // Double-linked list pointers. + ChromiumOpenInfo* pNext; + ChromiumOpenInfo* pPrev; +}; + +// Keep track of locks and inodes in double-linked lists. +static struct ChromiumLockInfo* lockList = 0; +static struct ChromiumOpenInfo* openList = 0; + +// Extension of sqlite3_file specific to the chromium VFS. +struct ChromiumFile { + sqlite3_io_methods const* pMethod; // Implementation of sqlite3_file. + ChromiumOpenInfo* pOpen; // Information about all open file descriptors for this file. + ChromiumLockInfo* pLock; // Information about all locks for this file. + int h; // File descriptor. + int dirfd; // File descriptor for the file directory. + unsigned char locktype; // Type of the lock used for this file. + int lastErrno; // Value of errno for last operation on this file. + ChromiumUnusedFd* pUnused; // Information about unused file descriptors for this file. +}; + +// The following constants specify the range of bytes used for locking. +// SQLiteSharedSize is the number of bytes available in the pool from which +// a random byte is selected for a shared lock. The pool of bytes for +// shared locks begins at SQLiteSharedFirstByte. +// The values are the same as used by SQLite for compatibility. +static const off_t SQLitePendingByte = 0x40000000; +static const off_t SQLiteReservedByte = SQLitePendingByte + 1; +static const off_t SQLiteSharedFirstByte = SQLitePendingByte + 2; +static const off_t SQLiteSharedSize = 510; + +// Maps a POSIX error code to an SQLite error code. +static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) +{ + switch (posixError) { + case 0: + return SQLITE_OK; + case EAGAIN: + case ETIMEDOUT: + case EBUSY: + case EINTR: + case ENOLCK: + return SQLITE_BUSY; + case EACCES: + // EACCES is like EAGAIN during locking operations. + if ((sqliteIOErr == SQLITE_IOERR_LOCK) || + (sqliteIOErr == SQLITE_IOERR_UNLOCK) || + (sqliteIOErr == SQLITE_IOERR_RDLOCK) || + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK)) + return SQLITE_BUSY; + return SQLITE_PERM; + case EPERM: + return SQLITE_PERM; + case EDEADLK: + return SQLITE_IOERR_BLOCKED; + default: + return sqliteIOErr; + } +} + +// Releases a ChromiumLockInfo structure previously allocated by findLockInfo(). +static void releaseLockInfo(ChromiumLockInfo* pLock) +{ + if (!pLock) + return; + + pLock->nRef--; + if (pLock->nRef > 0) + return; + + if (pLock->pPrev) { + ASSERT(pLock->pPrev->pNext == pLock); + pLock->pPrev->pNext = pLock->pNext; + } else { + ASSERT(lockList == pLock); + lockList = pLock->pNext; + } + if (pLock->pNext) { + ASSERT(pLock->pNext->pPrev == pLock); + pLock->pNext->pPrev = pLock->pPrev; + } + + sqlite3_free(pLock); +} + +// Releases a ChromiumOpenInfo structure previously allocated by findLockInfo(). +static void releaseOpenInfo(ChromiumOpenInfo* pOpen) +{ + if (!pOpen) + return; + + pOpen->nRef--; + if (pOpen->nRef > 0) + return; + + if (pOpen->pPrev) { + ASSERT(pOpen->pPrev->pNext == pOpen); + pOpen->pPrev->pNext = pOpen->pNext; + } else { + ASSERT(openList == pOpen); + openList = pOpen->pNext; + } + if (pOpen->pNext) { + ASSERT(pOpen->pNext->pPrev == pOpen); + pOpen->pNext->pPrev = pOpen->pPrev; + } + + ASSERT(!pOpen->pUnused); // Make sure we're not leaking memory and file descriptors. + + sqlite3_free(pOpen); +} + +// Locates ChromiumLockInfo and ChromiumOpenInfo for given file descriptor (creating new ones if needed). +// Returns a SQLite error code. +static int findLockInfo(ChromiumFile* pFile, ChromiumLockInfo** ppLock, ChromiumOpenInfo** ppOpen) +{ + int fd = pFile->h; + struct stat statbuf; + int rc = fstat(fd, &statbuf); + if (rc) { + pFile->lastErrno = errno; +#ifdef EOVERFLOW + if (pFile->lastErrno == EOVERFLOW) + return SQLITE_NOLFS; +#endif + return SQLITE_IOERR; + } + +#if OS(DARWIN) + // On OS X on an msdos/fat filesystems, the inode number is reported + // incorrectly for zero-size files. See http://www.sqlite.org/cvstrac/tktview?tn=3260. + // To work around this problem we always increase the file size to 1 by writing a single byte + // prior to accessing the inode number. The one byte written is an ASCII 'S' character which + // also happens to be the first byte in the header of every SQLite database. In this way, + // if there is a race condition such that another thread has already populated the first page + // of the database, no damage is done. + if (!statbuf.st_size) { + rc = write(fd, "S", 1); + if (rc != 1) + return SQLITE_IOERR; + rc = fstat(fd, &statbuf); + if (rc) { + pFile->lastErrno = errno; + return SQLITE_IOERR; + } + } +#endif + + ChromiumFileId fileId; + memset(&fileId, 0, sizeof(fileId)); + fileId.dev = statbuf.st_dev; + fileId.ino = statbuf.st_ino; + + ChromiumLockInfo* pLock = 0; + + if (ppLock) { + pLock = lockList; + while (pLock && memcmp(&fileId, &pLock->lockKey, sizeof(fileId))) + pLock = pLock->pNext; + if (pLock) + pLock->nRef++; + else { + pLock = static_cast<ChromiumLockInfo*>(sqlite3_malloc(sizeof(*pLock))); + if (!pLock) + return SQLITE_NOMEM; + pLock->lockKey = fileId; + pLock->nRef = 1; + pLock->cnt = 0; + pLock->locktype = 0; + pLock->pNext = lockList; + pLock->pPrev = 0; + if (lockList) + lockList->pPrev = pLock; + lockList = pLock; + } + *ppLock = pLock; + } + + if (ppOpen) { + ChromiumOpenInfo* pOpen = openList; + while (pOpen && memcmp(&fileId, &pOpen->fileId, sizeof(fileId))) + pOpen = pOpen->pNext; + if (pOpen) + pOpen->nRef++; + else { + pOpen = static_cast<ChromiumOpenInfo*>(sqlite3_malloc(sizeof(*pOpen))); + if (!pOpen) { + releaseLockInfo(pLock); + return SQLITE_NOMEM; + } + memset(pOpen, 0, sizeof(*pOpen)); + pOpen->fileId = fileId; + pOpen->nRef = 1; + pOpen->pNext = openList; + if (openList) + openList->pPrev = pOpen; + openList = pOpen; + } + *ppOpen = pOpen; + } + + return rc; +} + +// Checks if there is a RESERVED lock held on the specified file by this or any other process. +// If the lock is held, sets pResOut to a non-zero value. Returns a SQLite error code. +static int chromiumCheckReservedLock(sqlite3_file* id, int* pResOut) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + // Look for locks held by this process. + int reserved = 0; + if (pFile->pLock->locktype > SQLITE_LOCK_SHARED) + reserved = 1; + + // Look for locks held by other processes. + int rc = SQLITE_OK; + if (!reserved) { + struct flock lock; + lock.l_whence = SEEK_SET; + lock.l_start = SQLiteReservedByte; + lock.l_len = 1; + lock.l_type = F_WRLCK; + if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { + int tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); + pFile->lastErrno = tErrno; + } else if (lock.l_type != F_UNLCK) + reserved = 1; + } + + *pResOut = reserved; + return rc; +} + +// Performs a file locking operation on a range of bytes in a file. +// The |op| parameter should be one of F_RFLCK, F_WRLCK or F_UNLCK. +// Returns a Unix error code, and also writes it to pErrcode. +static int rangeLock(ChromiumFile* pFile, int op, int* pErrcode) +{ + struct flock lock; + lock.l_type = op; + lock.l_start = SQLiteSharedFirstByte; + lock.l_whence = SEEK_SET; + lock.l_len = SQLiteSharedSize; + int rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + return rc; +} + +// Locks the file with the lock specified by parameter locktype - one +// of the following: +// +// (1) SQLITE_LOCK_SHARED +// (2) SQLITE_LOCK_RESERVED +// (3) SQLITE_LOCK_PENDING +// (4) SQLITE_LOCK_EXCLUSIVE +// +// Sometimes when requesting one lock state, additional lock states +// are inserted in between. The locking might fail on one of the later +// transitions leaving the lock state different from what it started but +// still short of its goal. The following chart shows the allowed +// transitions and the inserted intermediate states: +// +// UNLOCKED -> SHARED +// SHARED -> RESERVED +// SHARED -> (PENDING) -> EXCLUSIVE +// RESERVED -> (PENDING) -> EXCLUSIVE +// PENDING -> EXCLUSIVE +static int chromiumLock(sqlite3_file* id, int locktype) +{ + // To obtain a SHARED lock, a read-lock is obtained on the 'pending + // byte'. If this is successful, a random byte from the 'shared byte + // range' is read-locked and the lock on the 'pending byte' released. + // + // A process may only obtain a RESERVED lock after it has a SHARED lock. + // A RESERVED lock is implemented by grabbing a write-lock on the + // 'reserved byte'. + // + // A process may only obtain a PENDING lock after it has obtained a + // SHARED lock. A PENDING lock is implemented by obtaining a write-lock + // on the 'pending byte'. This ensures that no new SHARED locks can be + // obtained, but existing SHARED locks are allowed to persist. A process + // does not have to obtain a RESERVED lock on the way to a PENDING lock. + // This property is used by the algorithm for rolling back a journal file + // after a crash. + // + // An EXCLUSIVE lock, obtained after a PENDING lock is held, is + // implemented by obtaining a write-lock on the entire 'shared byte + // range'. Since all other locks require a read-lock on one of the bytes + // within this range, this ensures that no other locks are held on the + // database. + + int rc = SQLITE_OK; + struct flock lock; + int s = 0; + int tErrno; + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + ChromiumLockInfo* pLock = pFile->pLock; + + // If there is already a lock of this type or more restrictive, do nothing. + if (pFile->locktype >= locktype) + return SQLITE_OK; + + // Make sure we never move from unlocked to anything higher than shared lock. + ASSERT(pFile->locktype != SQLITE_LOCK_NONE || locktype == SQLITE_LOCK_SHARED); + + // Make sure we never request a pending lock. + ASSERT(locktype != SQLITE_LOCK_PENDING); + + // Make sure a shared lock is always held when a RESERVED lock is requested. + ASSERT(locktype != SQLITE_LOCK_RESERVED || pFile->locktype == SQLITE_LOCK_SHARED); + + // If some thread using this PID has a lock via a different ChromiumFile + // handle that precludes the requested lock, return BUSY. + if (pFile->locktype != pLock->locktype && + (pLock->locktype >= SQLITE_LOCK_PENDING || locktype > SQLITE_LOCK_SHARED)) + return SQLITE_BUSY; + + // If a SHARED lock is requested, and some thread using this PID already + // has a SHARED or RESERVED lock, then just increment reference counts. + if (locktype == SQLITE_LOCK_SHARED && + (pLock->locktype == SQLITE_LOCK_SHARED || pLock->locktype == SQLITE_LOCK_RESERVED)) { + ASSERT(!pFile->locktype); + ASSERT(pLock->cnt > 0); + pFile->locktype = SQLITE_LOCK_SHARED; + pLock->cnt++; + pFile->pOpen->nLock++; + return SQLITE_OK; + } + + // A PENDING lock is needed before acquiring a SHARED lock and before + // acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will + // be released. + lock.l_len = 1; + lock.l_whence = SEEK_SET; + if (locktype == SQLITE_LOCK_SHARED || + (locktype == SQLITE_LOCK_EXCLUSIVE && pFile->locktype < SQLITE_LOCK_PENDING)) { + lock.l_type = (locktype == SQLITE_LOCK_SHARED ? F_RDLCK : F_WRLCK); + lock.l_start = SQLitePendingByte; + s = fcntl(pFile->h, F_SETLK, &lock); + if (s == -1) { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + return rc; + } + } + + if (locktype == SQLITE_LOCK_SHARED) { + ASSERT(!pLock->cnt); + ASSERT(!pLock->locktype); + + s = rangeLock(pFile, F_RDLCK, &tErrno); + + // Drop the temporary PENDING lock. + lock.l_start = SQLitePendingByte; + lock.l_len = 1; + lock.l_type = F_UNLCK; + if (fcntl(pFile->h, F_SETLK, &lock)) { + if (s != -1) { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + return rc; + } + } + if (s == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + } else { + pFile->locktype = SQLITE_LOCK_SHARED; + pFile->pOpen->nLock++; + pLock->cnt = 1; + } + } else if (locktype == SQLITE_LOCK_EXCLUSIVE && pLock->cnt > 1) { + // We are trying for an exclusive lock but another thread in the + // same process is still holding a shared lock. + rc = SQLITE_BUSY; + } else { + // The request was for a RESERVED or EXCLUSIVE lock. It is + // assumed that there is a SHARED or greater lock on the file + // already. + ASSERT(pFile->locktype); + lock.l_type = F_WRLCK; + switch (locktype) { + case SQLITE_LOCK_RESERVED: + lock.l_start = SQLiteReservedByte; + s = fcntl(pFile->h, F_SETLK, &lock); + tErrno = errno; + break; + case SQLITE_LOCK_EXCLUSIVE: + s = rangeLock(pFile, F_WRLCK, &tErrno); + break; + default: + ASSERT_NOT_REACHED(); + } + if (s == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + } + } + + if (rc == SQLITE_OK) { + pFile->locktype = locktype; + pLock->locktype = locktype; + } else if (locktype == SQLITE_LOCK_EXCLUSIVE) { + pFile->locktype = SQLITE_LOCK_PENDING; + pLock->locktype = SQLITE_LOCK_PENDING; + } + + return rc; +} + +// Closes all file descriptors for given ChromiumFile for which the close has been deferred. +// Returns a SQLite error code. +static int closePendingFds(ChromiumFile* pFile) +{ + int rc = SQLITE_OK; + ChromiumOpenInfo* pOpen = pFile->pOpen; + ChromiumUnusedFd* pError = 0; + ChromiumUnusedFd* pNext; + for (ChromiumUnusedFd* p = pOpen->pUnused; p; p = pNext) { + pNext = p->pNext; + if (close(p->fd)) { + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + p->pNext = pError; + pError = p; + } else + sqlite3_free(p); + } + pOpen->pUnused = pError; + return rc; +} + +// Lowers the locking level on file descriptor. +// locktype must be either SQLITE_LOCK_NONE or SQLITE_LOCK_SHARED. +static int chromiumUnlock(sqlite3_file* id, int locktype) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + ASSERT(locktype <= SQLITE_LOCK_SHARED); + + if (pFile->locktype <= locktype) + return SQLITE_OK; + + ChromiumLockInfo* pLock = pFile->pLock; + ASSERT(pLock->cnt); + + struct flock lock; + int rc = SQLITE_OK; + int h = pFile->h; + int tErrno; + + if (pFile->locktype > SQLITE_LOCK_SHARED) { + ASSERT(pLock->locktype == pFile->locktype); + + if (locktype == SQLITE_LOCK_SHARED && rangeLock(pFile, F_RDLCK, &tErrno) == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; + } + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SQLitePendingByte; + lock.l_len = 2; + if (fcntl(h, F_SETLK, &lock) != -1) + pLock->locktype = SQLITE_LOCK_SHARED; + else { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; + } + } + if (locktype == SQLITE_LOCK_NONE) { + struct ChromiumOpenInfo *pOpen; + + pLock->cnt--; + + // Release the lock using an OS call only when all threads in this same process have released the lock. + if (!pLock->cnt) { + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + if (fcntl(h, F_SETLK, &lock) != -1) + pLock->locktype = SQLITE_LOCK_NONE; + else { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + pLock->locktype = SQLITE_LOCK_NONE; + pFile->locktype = SQLITE_LOCK_NONE; + } + } + + pOpen = pFile->pOpen; + pOpen->nLock--; + ASSERT(pOpen->nLock >= 0); + if (!pOpen->nLock) { + int rc2 = closePendingFds(pFile); + if (rc == SQLITE_OK) + rc = rc2; + } + } + + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; } -// Chromium's Posix implementation of SQLite VFS -namespace { +// Closes all file handles for given ChromiumFile and sets all its fields to 0. +// Returns a SQLite error code. +static int chromiumCloseNoLock(sqlite3_file* id) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + if (!pFile) + return SQLITE_OK; + if (pFile->dirfd >= 0) { + if (close(pFile->dirfd)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + } + pFile->dirfd = -1; + } + if (pFile->h >= 0 && close(pFile->h)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + } + sqlite3_free(pFile->pUnused); + memset(pFile, 0, sizeof(ChromiumFile)); + return SQLITE_OK; +} + +// Closes a ChromiumFile, including locking operations. Returns a SQLite error code. +static int chromiumClose(sqlite3_file* id) +{ + if (!id) + return SQLITE_OK; + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + chromiumUnlock(id, SQLITE_LOCK_NONE); + if (pFile->pOpen && pFile->pOpen->nLock) { + // If there are outstanding locks, do not actually close the file just + // yet because that would clear those locks. + ChromiumOpenInfo* pOpen = pFile->pOpen; + ChromiumUnusedFd* p = pFile->pUnused; + p->pNext = pOpen->pUnused; + pOpen->pUnused = p; + pFile->h = -1; + pFile->pUnused = 0; + } + releaseLockInfo(pFile->pLock); + releaseOpenInfo(pFile->pOpen); + return chromiumCloseNoLock(id); +} + +static int chromiumCheckReservedLockNoop(sqlite3_file*, int* pResOut) +{ + *pResOut = 0; + return SQLITE_OK; +} + +static int chromiumLockNoop(sqlite3_file*, int) +{ + return SQLITE_OK; +} + +static int chromiumUnlockNoop(sqlite3_file*, int) +{ + return SQLITE_OK; +} + +// Seeks to the requested offset and reads up to |cnt| bytes into |pBuf|. Returns number of bytes actually read. +static int seekAndRead(ChromiumFile* id, sqlite3_int64 offset, void* pBuf, int cnt) +{ + sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); + if (newOffset != offset) { + id->lastErrno = (newOffset == -1) ? errno : 0; + return -1; + } + int got = read(id->h, pBuf, cnt); + if (got < 0) + id->lastErrno = errno; + return got; +} + +// Reads data from file into a buffer. Returns a SQLite error code. +static int chromiumRead(sqlite3_file* id, void* pBuf, int amt, sqlite3_int64 offset) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + // The bytes in the locking range should never be read. + ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); + + int got = seekAndRead(pFile, offset, pBuf, amt); + if (got == amt) + return SQLITE_OK; + + if (got < 0) + return SQLITE_IOERR_READ; + + // Unread parts of the buffer must be zero-filled. + memset(&(reinterpret_cast<char*>(pBuf))[got], 0, amt - got); + pFile->lastErrno = 0; + return SQLITE_IOERR_SHORT_READ; +} + +// Seeks to the requested offset and writes up to |cnt| bytes. Returns number of bytes actually written. +static int seekAndWrite(ChromiumFile* id, sqlite_int64 offset, const void* pBuf, int cnt) +{ + sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); + if (newOffset != offset) { + id->lastErrno = (newOffset == -1) ? errno : 0; + return -1; + } + int got = write(id->h, pBuf, cnt); + if (got < 0) + id->lastErrno = errno; + return got; +} + +// Writes data from buffer into a file. Returns a SQLite error code. +static int chromiumWrite(sqlite3_file* id, const void* pBuf, int amt, sqlite3_int64 offset) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + ASSERT(amt > 0); + + // The bytes in the locking range should never be written. + ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); + + int wrote = 0; + while (amt > 0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt)) > 0) { + amt -= wrote; + offset += wrote; + pBuf = &(reinterpret_cast<const char*>(pBuf))[wrote]; + } + if (amt > 0) { + if (wrote < 0) + return SQLITE_IOERR_WRITE; + pFile->lastErrno = 0; + return SQLITE_FULL; + } + return SQLITE_OK; +} + +static bool syncWrapper(int fd, bool fullSync) +{ +#if OS(DARWIN) + bool success = false; + if (fullSync) + success = !fcntl(fd, F_FULLFSYNC, 0); + if (!success) + success = !fsync(fd); + return success; +#else + return !fdatasync(fd); +#endif +} + +// Makes sure all writes to a particular file are committed to disk. Returns a SQLite error code. +static int chromiumSync(sqlite3_file* id, int flags) +{ + ASSERT((flags & 0x0F) == SQLITE_SYNC_NORMAL || (flags & 0x0F) == SQLITE_SYNC_FULL); + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + bool isFullSync = ((flags & 0x0F) == SQLITE_SYNC_FULL); + + if (!syncWrapper(pFile->h, isFullSync)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_FSYNC; + } + + if (pFile->dirfd >= 0) { +#if !OS(DARWIN) + if (!isFullSync) { + // Ignore directory sync failures, see http://www.sqlite.org/cvstrac/tktview?tn=1657. + syncWrapper(pFile->dirfd, false); + } +#endif + if (!close(pFile->dirfd)) + pFile->dirfd = -1; + else { + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + } + } + + return SQLITE_OK; +} + +// Truncates an open file to the specified size. Returns a SQLite error code. +static int chromiumTruncate(sqlite3_file* id, sqlite_int64 nByte) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + if (ftruncate(pFile->h, nByte)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_TRUNCATE; + } + + return SQLITE_OK; +} + +// Determines the size of a file in bytes. Returns a SQLite error code. +static int chromiumFileSize(sqlite3_file* id, sqlite_int64* pSize) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + struct stat buf; + if (fstat(pFile->h, &buf)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_FSTAT; + } + *pSize = buf.st_size; + + // When opening a zero-size database, findLockInfo writes a single byte into that file + // in order to work around a bug in the OS X msdos filesystem. In order to avoid problems + // with upper layers, we need to report this file size as zero even though it is really 1. + // See http://www.sqlite.org/cvstrac/tktview?tn=3260. + if (*pSize == 1) + *pSize = 0; + + return SQLITE_OK; +} + +static int chromiumFileControl(sqlite3_file* id, int op, void* pArg) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + switch (op) { + case SQLITE_FCNTL_LOCKSTATE: + *reinterpret_cast<int*>(pArg) = pFile->locktype; + return SQLITE_OK; + case SQLITE_LAST_ERRNO: + *reinterpret_cast<int*>(pArg) = pFile->lastErrno; + return SQLITE_OK; + } + return SQLITE_ERROR; +} + +// Same as SQLITE_DEFAULT_SECTOR_SIZE from sqlite's os.h. +static const int SQLiteDefaultSectorSize = 512; + +static int chromiumSectorSize(sqlite3_file*) +{ + return SQLiteDefaultSectorSize; +} + +static int chromiumDeviceCharacteristics(sqlite3_file*) +{ + return 0; +} + +static const sqlite3_io_methods posixIoMethods = { + 1, + chromiumClose, + chromiumRead, + chromiumWrite, + chromiumTruncate, + chromiumSync, + chromiumFileSize, + chromiumLock, + chromiumUnlock, + chromiumCheckReservedLock, + chromiumFileControl, + chromiumSectorSize, + chromiumDeviceCharacteristics +}; + +static const sqlite3_io_methods nolockIoMethods = { + 1, + chromiumCloseNoLock, + chromiumRead, + chromiumWrite, + chromiumTruncate, + chromiumSync, + chromiumFileSize, + chromiumLockNoop, + chromiumUnlockNoop, + chromiumCheckReservedLockNoop, + chromiumFileControl, + chromiumSectorSize, + chromiumDeviceCharacteristics +}; + +// Initializes a ChromiumFile. Returns a SQLite error code. +static int fillInChromiumFile(sqlite3_vfs* pVfs, int h, int dirfd, sqlite3_file* pId, const char* zFilename, int noLock) +{ + ChromiumFile* pNew = reinterpret_cast<ChromiumFile*>(pId); + + ASSERT(!pNew->pLock); + ASSERT(!pNew->pOpen); + + pNew->h = h; + pNew->dirfd = dirfd; + + int rc = SQLITE_OK; + const sqlite3_io_methods* pLockingStyle; + if (noLock) + pLockingStyle = &nolockIoMethods; + else { + pLockingStyle = &posixIoMethods; + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if (rc != SQLITE_OK) { + // If an error occured in findLockInfo(), close the file descriptor + // immediately. This can happen in two scenarios: + // + // (a) A call to fstat() failed. + // (b) A malloc failed. + // + // Scenario (b) may only occur if the process is holding no other + // file descriptors open on the same file. If there were other file + // descriptors on this file, then no malloc would be required by + // findLockInfo(). If this is the case, it is quite safe to close + // handle h - as it is guaranteed that no posix locks will be released + // by doing so. + // + // If scenario (a) caused the error then things are not so safe. The + // implicit assumption here is that if fstat() fails, things are in + // such bad shape that dropping a lock or two doesn't matter much. + close(h); + h = -1; + } + } + + pNew->lastErrno = 0; + if (rc != SQLITE_OK) { + if (dirfd >= 0) + close(dirfd); + if (h >= 0) + close(h); + } else + pNew->pMethod = pLockingStyle; + return rc; +} + +// Searches for an unused file descriptor that was opened on the database +// file identified by zPath with matching flags. Returns 0 if not found. +static ChromiumUnusedFd* findReusableFd(const char* zPath, int flags) +{ + ChromiumUnusedFd* pUnused = 0; + + struct stat sStat; + if (!stat(zPath, &sStat)) { + ChromiumFileId id; + id.dev = sStat.st_dev; + id.ino = sStat.st_ino; + + ChromiumOpenInfo* pO = 0; + for (pO = openList; pO && memcmp(&id, &pO->fileId, sizeof(id)); pO = pO->pNext) { } + if (pO) { + ChromiumUnusedFd** pp; + for (pp = &pO->pUnused; *pp && (*pp)->flags != flags; pp = &((*pp)->pNext)) { } + pUnused = *pp; + if (pUnused) + *pp = pUnused->pNext; + } + } + return pUnused; +} // Opens a file. // @@ -59,14 +974,26 @@ namespace { // id - the structure that will manipulate the newly opened file. // desiredFlags - the desired open mode flags. // usedFlags - the actual open mode flags that were used. -int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, - sqlite3_file* id, int desiredFlags, int* usedFlags) +static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, + sqlite3_file* id, int desiredFlags, int* usedFlags) { - chromium_sqlite3_initialize_unix_sqlite3_file(id); + // The mask 0x00007F00 gives us the 7 bits that determine the type of the file SQLite is trying to open. + int fileType = desiredFlags & 0x00007F00; + + memset(id, 0, sizeof(ChromiumFile)); + ChromiumFile* chromiumFile = reinterpret_cast<ChromiumFile*>(id); int fd = -1; - int result = chromium_sqlite3_get_reusable_file_handle(id, fileName, desiredFlags, &fd); - if (result != SQLITE_OK) - return result; + if (fileType == SQLITE_OPEN_MAIN_DB) { + ChromiumUnusedFd* unusedFd = findReusableFd(fileName, desiredFlags); + if (unusedFd) + fd = unusedFd->fd; + else { + unusedFd = static_cast<ChromiumUnusedFd*>(sqlite3_malloc(sizeof(*unusedFd))); + if (!unusedFd) + return SQLITE_NOMEM; + } + chromiumFile->pUnused = unusedFd; + } if (fd < 0) { fd = ChromiumBridge::databaseOpenFile(fileName, desiredFlags); @@ -76,23 +1003,24 @@ int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, } } if (fd < 0) { - chromium_sqlite3_destroy_reusable_file_handle(id); + sqlite3_free(chromiumFile->pUnused); return SQLITE_CANTOPEN; } if (usedFlags) *usedFlags = desiredFlags; - chromium_sqlite3_update_reusable_file_handle(id, fd, desiredFlags); + if (chromiumFile->pUnused) { + chromiumFile->pUnused->fd = fd; + chromiumFile->pUnused->flags = desiredFlags; + } fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); - // The mask 0x00007F00 gives us the 7 bits that determine the type of the file SQLite is trying to open. - int fileType = desiredFlags & 0x00007F00; int noLock = (fileType != SQLITE_OPEN_MAIN_DB); - result = chromium_sqlite3_fill_in_unix_sqlite3_file(vfs, fd, -1, id, fileName, noLock); - if (result != SQLITE_OK) - chromium_sqlite3_destroy_reusable_file_handle(id); - return result; + int rc = fillInChromiumFile(vfs, fd, -1, id, fileName, noLock); + if (rc != SQLITE_OK) + sqlite3_free(chromiumFile->pUnused); + return rc; } // Deletes the given file. @@ -101,7 +1029,7 @@ int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, // fileName - the name of the file. // syncDir - determines if the directory to which this file belongs // should be synched after the file is deleted. -int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) +static int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) { return ChromiumBridge::databaseDeleteFile(fileName, syncDir); } @@ -112,7 +1040,7 @@ int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) // fileName - the name of the file. // flag - the type of test to make on this file. // res - the result. -int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) +static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) { int attr = static_cast<int>(ChromiumBridge::databaseGetFileAttributes(fileName)); if (attr < 0) { @@ -122,7 +1050,7 @@ int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) switch (flag) { case SQLITE_ACCESS_EXISTS: - *res = 1; // if the file doesn't exist, attr < 0 + *res = 1; // if the file doesn't exist, attr < 0 break; case SQLITE_ACCESS_READWRITE: *res = (attr & W_OK) && (attr & R_OK); @@ -143,8 +1071,8 @@ int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) // relativePath - the relative path. // bufSize - the size of the output buffer in bytes. // absolutePath - the output buffer where the absolute path will be stored. -int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, - int, char* absolutePath) +static int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, + int, char* absolutePath) { // The renderer process doesn't need to know the absolute path of the file sqlite3_snprintf(vfs->mxPathname, absolutePath, "%s", relativePath); @@ -152,44 +1080,107 @@ int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, } #ifndef SQLITE_OMIT_LOAD_EXTENSION -// Returns NULL, thus disallowing loading libraries in the renderer process. -// -// vfs - pointer to the sqlite3_vfs object. -// fileName - the name of the shared library file. -void* chromiumDlOpen(sqlite3_vfs*, const char*) +// We disallow loading DSOs inside the renderer process, so the following procedures are no-op. +static void* chromiumDlOpen(sqlite3_vfs*, const char*) +{ + return 0; +} + +static void chromiumDlError(sqlite3_vfs*, int, char*) +{ +} + +static void (*chromiumDlSym(sqlite3_vfs*, void*, const char*))() { return 0; } + +static void chromiumDlClose(sqlite3_vfs*, void*) +{ +} #else #define chromiumDlOpen 0 +#define chromiumDlError 0 +#define chromiumDlSym 0 +#define chromiumDlClose 0 #endif // SQLITE_OMIT_LOAD_EXTENSION -} // namespace +// Generates a seed for SQLite's PRNG. +static int chromiumRandomness(sqlite3_vfs*, int nBuf, char *zBuf) +{ + ASSERT(static_cast<size_t>(nBuf) >= (sizeof(time_t) + sizeof(int))); + + memset(zBuf, 0, nBuf); + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + time_t t; + time(&t); + memcpy(zBuf, &t, sizeof(t)); + int pid = getpid(); + memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); + return sizeof(t) + sizeof(pid); + } + nBuf = read(fd, zBuf, nBuf); + close(fd); + return nBuf; +} + +// Sleeps for at least |microseconds|, and returns the actual +// amount of time spent sleeping (in microseconds). +static int chromiumSleep(sqlite3_vfs*, int microseconds) +{ +#if OS(DARWIN) + usleep(microseconds); + return microseconds; +#else + // Round to the nearest second. + int seconds = (microseconds + 999999) / 1000000; + sleep(seconds); + return seconds * 1000000; +#endif +} + +// Retrieves the current system time (UTC). +static int chromiumCurrentTime(sqlite3_vfs*, double* now) +{ + struct timeval timeval; + gettimeofday(&timeval, 0); + *now = 2440587.5 + timeval.tv_sec / 86400.0 + timeval.tv_usec / 86400000000.0; + return 0; +} + +// This is not yet implemented in SQLite core. +static int chromiumGetLastError(sqlite3_vfs*, int, char*) +{ + return 0; +} + +// Same as MAX_PATHNAME from sqlite's os_unix.c. +static const int chromiumMaxPathname = 512; namespace WebCore { void SQLiteFileSystem::registerSQLiteVFS() { - sqlite3_vfs* unix_vfs = sqlite3_vfs_find("unix"); static sqlite3_vfs chromium_vfs = { 1, - unix_vfs->szOsFile, - unix_vfs->mxPathname, + sizeof(ChromiumFile), + chromiumMaxPathname, 0, "chromium_vfs", - unix_vfs->pAppData, + 0, chromiumOpen, chromiumDelete, chromiumAccess, chromiumFullPathname, chromiumDlOpen, - unix_vfs->xDlError, - unix_vfs->xDlSym, - unix_vfs->xDlClose, - unix_vfs->xRandomness, - unix_vfs->xSleep, - unix_vfs->xCurrentTime, - unix_vfs->xGetLastError + chromiumDlError, + chromiumDlSym, + chromiumDlClose, + chromiumRandomness, + chromiumSleep, + chromiumCurrentTime, + chromiumGetLastError }; sqlite3_vfs_register(&chromium_vfs, 0); } |