// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/database/vfs_backend.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" #include "third_party/sqlite/sqlite3.h" namespace webkit_database { static const int kFileTypeMask = 0x00007F00; // static void VfsBackend::GetFileHandleForProcess(base::ProcessHandle process_handle, const base::PlatformFile& file_handle, base::PlatformFile* target_handle, bool close_source_handle) { if (file_handle == base::kInvalidPlatformFileValue) { *target_handle = base::kInvalidPlatformFileValue; return; } #if defined(OS_WIN) // Duplicate the file handle. if (!DuplicateHandle(GetCurrentProcess(), file_handle, process_handle, target_handle, 0, false, DUPLICATE_SAME_ACCESS | (close_source_handle ? DUPLICATE_CLOSE_SOURCE : 0))) { // file_handle is closed whether or not DuplicateHandle succeeds. *target_handle = INVALID_HANDLE_VALUE; } #elif defined(OS_POSIX) *target_handle = file_handle; #endif } // static bool VfsBackend::FileTypeIsMainDB(int desired_flags) { return (desired_flags & kFileTypeMask) == SQLITE_OPEN_MAIN_DB; } // static bool VfsBackend::FileTypeIsJournal(int desired_flags) { int file_type = desired_flags & kFileTypeMask; return ((file_type == SQLITE_OPEN_MAIN_JOURNAL) || (file_type == SQLITE_OPEN_TEMP_JOURNAL) || (file_type == SQLITE_OPEN_SUBJOURNAL) || (file_type == SQLITE_OPEN_MASTER_JOURNAL)); } // static bool VfsBackend::OpenTypeIsReadWrite(int desired_flags) { return (desired_flags & SQLITE_OPEN_READWRITE) != 0; } // static bool VfsBackend::OpenFileFlagsAreConsistent(int desired_flags) { const int file_type = desired_flags & kFileTypeMask; const bool is_exclusive = (desired_flags & SQLITE_OPEN_EXCLUSIVE) != 0; const bool is_delete = (desired_flags & SQLITE_OPEN_DELETEONCLOSE) != 0; const bool is_create = (desired_flags & SQLITE_OPEN_CREATE) != 0; const bool is_read_only = (desired_flags & SQLITE_OPEN_READONLY) != 0; const bool is_read_write = (desired_flags & SQLITE_OPEN_READWRITE) != 0; // All files should be opened either read-write or read-only, but not both. if (is_read_only == is_read_write) return false; // If a new file is created, it must also be writable. if (is_create && !is_read_write) return false; // If we're accessing an existing file, we cannot give exclusive access, and // we can't delete it. // Normally, we'd also check that 'is_delete' is false for a main DB, main // journal or master journal file; however, when in incognito mode, we use // the SQLITE_OPEN_DELETEONCLOSE flag when opening those files too and keep // an open handle to them for as long as the incognito profile is around. if ((is_exclusive || is_delete) && !is_create) return false; // Make sure we're opening the DB directory or that a file type is set. return (file_type == SQLITE_OPEN_MAIN_DB) || (file_type == SQLITE_OPEN_TEMP_DB) || (file_type == SQLITE_OPEN_MAIN_JOURNAL) || (file_type == SQLITE_OPEN_TEMP_JOURNAL) || (file_type == SQLITE_OPEN_SUBJOURNAL) || (file_type == SQLITE_OPEN_MASTER_JOURNAL) || (file_type == SQLITE_OPEN_TRANSIENT_DB); } // static void VfsBackend::OpenFile(const FilePath& file_path, int desired_flags, base::PlatformFile* file_handle) { DCHECK(!file_path.empty()); // Verify the flags for consistency and create the database // directory if it doesn't exist. if (!OpenFileFlagsAreConsistent(desired_flags) || !file_util::CreateDirectory(file_path.DirName())) return; int flags = 0; flags |= base::PLATFORM_FILE_READ; if (desired_flags & SQLITE_OPEN_READWRITE) flags |= base::PLATFORM_FILE_WRITE; if (!(desired_flags & SQLITE_OPEN_MAIN_DB)) { flags |= base::PLATFORM_FILE_EXCLUSIVE_READ | base::PLATFORM_FILE_EXCLUSIVE_WRITE; } flags |= ((desired_flags & SQLITE_OPEN_CREATE) ? base::PLATFORM_FILE_OPEN_ALWAYS : base::PLATFORM_FILE_OPEN); if (desired_flags & SQLITE_OPEN_EXCLUSIVE) { flags |= base::PLATFORM_FILE_EXCLUSIVE_READ | base::PLATFORM_FILE_EXCLUSIVE_WRITE; } if (desired_flags & SQLITE_OPEN_DELETEONCLOSE) { flags |= base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_DELETE_ON_CLOSE; } // Try to open/create the DB file. *file_handle = base::CreatePlatformFile(file_path, flags, NULL, NULL); } // static void VfsBackend::OpenTempFileInDirectory( const FilePath& dir_path, int desired_flags, base::PlatformFile* file_handle) { // We should be able to delete temp files when they're closed // and create them as needed if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE) || !(desired_flags & SQLITE_OPEN_CREATE)) { return; } // Get a unique temp file name in the database directory. FilePath temp_file_path; if (!file_util::CreateTemporaryFileInDir(dir_path, &temp_file_path)) return; OpenFile(temp_file_path, desired_flags, file_handle); } // static int VfsBackend::DeleteFile(const FilePath& file_path, bool sync_dir) { if (!file_util::PathExists(file_path)) return SQLITE_OK; if (!file_util::Delete(file_path, false)) return SQLITE_IOERR_DELETE; int error_code = SQLITE_OK; #if defined(OS_POSIX) if (sync_dir) { base::PlatformFile dir_fd = base::CreatePlatformFile( file_path.DirName(), base::PLATFORM_FILE_READ, NULL, NULL); if (dir_fd == base::kInvalidPlatformFileValue) { error_code = SQLITE_CANTOPEN; } else { if (fsync(dir_fd)) error_code = SQLITE_IOERR_DIR_FSYNC; base::ClosePlatformFile(dir_fd); } } #endif return error_code; } // static uint32 VfsBackend::GetFileAttributes(const FilePath& file_path) { #if defined(OS_WIN) uint32 attributes = ::GetFileAttributes(file_path.value().c_str()); #elif defined(OS_POSIX) uint32 attributes = 0; if (!access(file_path.value().c_str(), R_OK)) attributes |= static_cast(R_OK); if (!access(file_path.value().c_str(), W_OK)) attributes |= static_cast(W_OK); if (!attributes) attributes = -1; #endif return attributes; } // static int64 VfsBackend::GetFileSize(const FilePath& file_path) { int64 size = 0; return (file_util::GetFileSize(file_path, &size) ? size : 0); } } // namespace webkit_database