| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "SQLiteFileSystem.h" |
| |
| #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; |
| |
| // 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; |
| } |
| |
| // 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. |
| // |
| // vfs - pointer to the sqlite3_vfs object. |
| // fileName - the name of the file. |
| // 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. |
| static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, |
| sqlite3_file* id, int desiredFlags, int* usedFlags) |
| { |
| // 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; |
| 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); |
| if ((fd < 0) && (desiredFlags & SQLITE_OPEN_READWRITE)) { |
| int newFlags = (desiredFlags & ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) | SQLITE_OPEN_READONLY; |
| fd = ChromiumBridge::databaseOpenFile(fileName, newFlags); |
| } |
| } |
| if (fd < 0) { |
| sqlite3_free(chromiumFile->pUnused); |
| return SQLITE_CANTOPEN; |
| } |
| |
| if (usedFlags) |
| *usedFlags = desiredFlags; |
| if (chromiumFile->pUnused) { |
| chromiumFile->pUnused->fd = fd; |
| chromiumFile->pUnused->flags = desiredFlags; |
| } |
| |
| fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); |
| |
| int noLock = (fileType != SQLITE_OPEN_MAIN_DB); |
| int rc = fillInChromiumFile(vfs, fd, -1, id, fileName, noLock); |
| if (rc != SQLITE_OK) |
| sqlite3_free(chromiumFile->pUnused); |
| return rc; |
| } |
| |
| // Deletes the given file. |
| // |
| // vfs - pointer to the sqlite3_vfs object. |
| // fileName - the name of the file. |
| // syncDir - determines if the directory to which this file belongs |
| // should be synched after the file is deleted. |
| static int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) |
| { |
| return ChromiumBridge::databaseDeleteFile(fileName, syncDir); |
| } |
| |
| // Check the existance and status of the given file. |
| // |
| // vfs - pointer to the sqlite3_vfs object. |
| // fileName - the name of the file. |
| // flag - the type of test to make on this file. |
| // res - the result. |
| static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) |
| { |
| int attr = static_cast<int>(ChromiumBridge::databaseGetFileAttributes(fileName)); |
| if (attr < 0) { |
| *res = 0; |
| return SQLITE_OK; |
| } |
| |
| switch (flag) { |
| case SQLITE_ACCESS_EXISTS: |
| *res = 1; // if the file doesn't exist, attr < 0 |
| break; |
| case SQLITE_ACCESS_READWRITE: |
| *res = (attr & W_OK) && (attr & R_OK); |
| break; |
| case SQLITE_ACCESS_READ: |
| *res = (attr & R_OK); |
| break; |
| default: |
| return SQLITE_ERROR; |
| } |
| |
| return SQLITE_OK; |
| } |
| |
| // Turns a relative pathname into a full pathname. |
| // |
| // vfs - pointer to the sqlite3_vfs object. |
| // relativePath - the relative path. |
| // bufSize - the size of the output buffer in bytes. |
| // absolutePath - the output buffer where the absolute path will be stored. |
| 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); |
| return SQLITE_OK; |
| } |
| |
| #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| // 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 |
| |
| // 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() |
| { |
| static sqlite3_vfs chromium_vfs = { |
| 1, |
| sizeof(ChromiumFile), |
| chromiumMaxPathname, |
| 0, |
| "chromium_vfs", |
| 0, |
| chromiumOpen, |
| chromiumDelete, |
| chromiumAccess, |
| chromiumFullPathname, |
| chromiumDlOpen, |
| chromiumDlError, |
| chromiumDlSym, |
| chromiumDlClose, |
| chromiumRandomness, |
| chromiumSleep, |
| chromiumCurrentTime, |
| chromiumGetLastError |
| }; |
| sqlite3_vfs_register(&chromium_vfs, 0); |
| } |
| |
| } // namespace WebCore |